using CD67.ModeleMVC.Entity;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace CD67.ModeleMVC.Factory.Internal
{
    /// <summary>
    /// Classe de base pour toutes les classes spécialisées de la couche de service
    /// </summary>
    public class BaseFactory<T> : IDisposable where T : class
    {
        /// <summary>
        /// Context Entity Framework utilisé dans la classe
        /// </summary>
        protected Entities dbContext;

        #region Constructeurs
        /// <summary>
        /// Constructeur sans argument pour les classes sans contexts Entity
        /// </summary>
        public BaseFactory() { }

        /// <summary>
        /// Constructeur avec initialisation du context Entity Framework
        /// </summary>
        /// <param name="dbContext">Context Entity Framework</param>
        public BaseFactory(Entities dbContext)
        {
            this.dbContext = dbContext;
        }
        #endregion

        #region Méthodes génériques
        private DbSet<T> ObjectSet
        {
            get
            {
                return this.dbContext.Set<T>();
            }
        }

        public virtual T getById(params object[] keyValues)
        {
            return ObjectSet.Find(keyValues);
        }

        public virtual T getBy(Expression<Func<T, bool>> expression)
        {
            return ObjectSet.Where(expression).FirstOrDefault();
        }

        public virtual T getBy(string dynamicExpression)
        {
            return ObjectSet.Where(dynamicExpression).FirstOrDefault();
        }

        public virtual IQueryable<T> getAll()
        {
            return getAll(null);
        }

        public virtual IQueryable<T> getAll(string sortParameter = null)
        {
            if (sortParameter != null) return ObjectSet.OrderBy(sortParameter);
            else return ObjectSet;
        }

        public IQueryable<T> where(Expression<Func<T, bool>> expression, string sortParameter = null)
        {
            IQueryable<T> query = ObjectSet.Where(expression);
            if (sortParameter != null) query = query.OrderBy(sortParameter);
            return query;
        }

        public IQueryable<T> where(string dynamicExpression, string sortParameter = null)
        {
            IQueryable<T> query = ObjectSet.Where(dynamicExpression);
            if (sortParameter != null) query = query.OrderBy(sortParameter);
            return query;
        }

        public void add(ref T entity)
        {
            ObjectSet.Add(entity);
            dbContext.SaveChanges();
        }

        public void update(ref T entity)
        {
            // On attache l'objet en paramètre au contexte, on le note bien comme modifié pour qu'il soit mis à jour
            ObjectSet.Attach(entity);
            dbContext.Entry(entity).State = EntityState.Modified;
            dbContext.SaveChanges();
        }

        public void delete(ref T entity)
        {
            ObjectSet.Remove(entity);
            dbContext.SaveChanges();
        }
        #endregion

        #region Dispose
        private bool disposed = false;

        /// <summary>
        /// Fonction pour détruire proprement la classe après utilisation
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    dbContext.Dispose();
                }
            }
            this.disposed = true;
        }

        /// <summary>
        /// Fonction pour détruire proprement la classe après utilisation
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}
