using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms.VisualStyles;
using BeWo.Data.Entities;

using BS.Shared;

using NHibernate;
using NHibernate.Bytecode;
using NHibernate.Criterion;
using NHibernate.Proxy;

namespace BeWo.Data.Access
{
    public class GenericDAO : AbstractBaseDAO
    {
        internal GenericDAO()
        {
        }

        public virtual void Deactivate(BeWoEntityBase pEntity)
        {
            this.SetActivationType(pEntity, ActivationTypeId.Deleted);
        }

        public virtual void Deactivate<T>(IEnumerable<T> pEntitys) where T : BeWoEntityBase, new()
        {
            ITransaction lTransaction = this.Session.BeginTransaction();
            try
            {
                foreach (BeWoEntityBase iEntity in pEntitys)
                {
                    iEntity.IsActive = ActivationTypeId.Deleted;
                    if (iEntity.SystemEntryID == null)
                    {
                        this.Session.SaveOrUpdate(iEntity);
                    }
                    else
                    {
                        throw new InvalidOperationException("Can´t delete System Entries");
                    }
                }

                lTransaction.Commit();
            }
            catch (Exception e)
            {
                lTransaction.Rollback();
                throw e;
            }
        }

        public virtual void Delete(BeWoEntityBase pEntity)
        {
            if (pEntity.SystemEntryID == null)
            {
                ITransaction lTransaction = this.Session.BeginTransaction();
                try
                {
                    this.Session.Delete(pEntity);
                    lTransaction.Commit();
                }
                catch (Exception e)
                {
                    lTransaction.Rollback();
                    throw e;
                }
            }
            else
            {
                throw new InvalidOperationException("Can´t delete System Entries");
            }
        }

        public virtual void Delete<T>(IEnumerable<T> pEntitys) where T : BeWoEntityBase, new()
        {
            ITransaction lTransaction = this.Session.BeginTransaction();
            try
            {
                foreach (BeWoEntityBase iEntity in pEntitys)
                {
					if (iEntity.SystemEntryID == null || iEntity.SystemEntryID.Value == SystemEntryID.AdditionalServiceAssessmentSheet)
                    {
                        this.Session.Delete(iEntity);
                    }
                    else
                    {
						throw new InvalidOperationException("Can´t delete System Entries");
                    }
                }

                lTransaction.Commit();
            }
            catch (Exception e)
            {
                lTransaction.Rollback();
                throw e;
            }
        }

        public virtual IList<T> GetAll<T>() where T : BeWoEntityBase
        {
            return this.CreateCriteria<T>().List<T>();
        }

        public virtual IList<T> GetAllActive<T>() where T : BeWoEntityBase
        {
            return this.GetAllWithActivationType<T>(ActivationTypeId.Active);
        }

        public virtual IList<T> GetAllActiveAndArchived<T>() where T : BeWoEntityBase
        {
            return this.CreateCriteria<T>().Add(Restrictions.Or(Restrictions.Eq(BeWoEntityBase.PropertyName_IsActive, ActivationTypeId.Active), Restrictions.Eq(BeWoEntityBase.PropertyName_IsActive, ActivationTypeId.Archived))).List<T>();
        }

        public virtual IList<T> GetAllWithActivationType<T>(ActivationTypeId activationType) where T : BeWoEntityBase
        {
			return this.CreateCriteria<T>().Add(Restrictions.Eq(BeWoEntityBase.PropertyName_IsActive, activationType)).List<T>();
        }

        public virtual T GetByID<T>(long pOid) where T : BeWoEntityBase, new()
        {
            return this.Session.Get<T>(pOid);
        }

		public virtual IList<T> GetActiveByIDs<T>(IEnumerable<long> pOids) where T : BeWoEntityBase, new()
	    {
		    return CreateCriteria<T>().Add(Restrictions.Eq(BeWoEntityBase.PropertyName_IsActive, ActivationTypeId.Active)).Add(Restrictions.In(BeWoEntityBase.PropertyName_Oid, pOids.ToArray())).List<T>();
	    }

        public virtual void Insert(BeWoEntityBase pEntity)
        {
            try
            {
                ISession session = Session;
                if (session == null)
                    throw new InvalidOperationException("GenericDAO.Insert(): Session is null");
                ITransaction lTransaction = session.BeginTransaction();
                
                try
                {
                    pEntity.IsActive = ActivationTypeId.Active;
                    session.Save(pEntity);
                    lTransaction.Commit();
                }
                catch (Exception e)
                {
                    lTransaction.Rollback();
                    throw e;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public virtual void Insert<T>(IEnumerable<T> pEntitys) where T : BeWoEntityBase, new()
        {
            ITransaction lTransaction = this.Session.BeginTransaction();
            try
            {
                foreach (BeWoEntityBase iEntity in pEntitys)
                {
                    this.Session.Save(iEntity);
                }

                lTransaction.Commit();
            }
            catch (Exception e)
            {
                lTransaction.Rollback();
                throw e;
            }
        }

        public virtual T LoadByID<T>(long pOid) where T : BeWoEntityBase, new()
        {
            return this.Session.Load<T>(pOid);
        }

        public virtual List<T> LoadByIDs<T>(IEnumerable<long> pOids) where T : BeWoEntityBase, new()
        {
            return pOids.Select(oid => this.LoadByID<T>(oid)).ToList();
        }

        public virtual T LoadSystemEntry<T>(SystemEntryID pId) where T : BeWoEntityBase, new()
        {
            return this.CreateCriteria<T>().Add(Restrictions.Eq(BeWoEntityBase.PropertyName_SystemEntryID, pId)).UniqueResult<T>();
        }

        public void Reattach<T>(T entity) where T : BeWoEntityBase, new()
        {
            this.Session.Lock(entity, LockMode.None);
        }

        public virtual void SetActivationType(BeWoEntityBase pEntity, ActivationTypeId activationType)
        {
            if (pEntity.SystemEntryID == null)
            {
                ITransaction lTransaction = this.Session.BeginTransaction();
                try
                {
                    pEntity.IsActive = activationType;
                    this.Session.SaveOrUpdate(pEntity);
                    lTransaction.Commit();
                }
                catch (Exception e)
                {
                    lTransaction.Rollback();
                    throw e;
                }
            }
            else
            {
                throw new InvalidOperationException("Can´t delete System Entries");
            }
        }

        public virtual void Update(BeWoEntityBase pEntity)
        {
            ITransaction lTransaction = this.Session.BeginTransaction();
            try
            {
                this.Session.SaveOrUpdate(pEntity);
                lTransaction.Commit();
            }
            catch (Exception e)
            {
                lTransaction.Rollback();
                throw e;
            }
        }

        public virtual void Update<T>(IEnumerable<T> pEntities) where T : BeWoEntityBase, new()
        {
            ITransaction lTransaction = this.Session.BeginTransaction();
            try
            {
                foreach (BeWoEntityBase iEntity in pEntities)
                {
                    this.Session.SaveOrUpdate(iEntity);
                }

                lTransaction.Commit();
            }
            catch (Exception e)
            {
                lTransaction.Rollback();
                throw e;
            }
        }

        public virtual T Unproxy<T>(T obj)
        {
            if (!NHibernateUtil.IsInitialized(obj))
            {
                NHibernateUtil.Initialize(obj);
            }

            if (obj is INHibernateProxy)
            {
                return (T)this.Session.GetSessionImplementation().PersistenceContext.Unproxy(obj);
            }
            return obj;
        }
        
	}
}