using System.Collections.Generic;
using System.Linq;

using BeWo.Data.Entities;
using BeWo.Service.Core;

using BS.Shared.Extensions;

namespace BeWo.Service.DCEntityMapper
{
    public abstract class AbstractIDCEntityMapper<TEntity, TDC> : IDCEntityMapper<TEntity, TDC>
        where TDC : new() where TEntity : BeWoEntityBase, new()
    {
        public bool ConcurrencyCheck(long? pDataContractVersion, BeWoEntityBase pEntity)
        {
            return ServiceLogic.ConcurrencyCheck(pDataContractVersion, pEntity);
        }

        public virtual void MergeWithEntitys(IList<TDC> pDataContractList, IList<TEntity> pEntityList)
        {
            if (pEntityList.Count == 0 && pDataContractList != null && pDataContractList.Count > 0)
            {
                pEntityList.AddRange(pDataContractList.Select(dc => this.MapToNewEntity(dc)));
                return;
            }

            if (pDataContractList == null || pDataContractList.Count == 0)
            {
                pEntityList.Clear();
                return;
            }

            // remove deleted
            pEntityList.RemoveRange(entity => pDataContractList.Count(dc => this.AreDCAndEntityEqual(dc, entity)) == 0);

            // merge same
            var lToMerge = pEntityList.Select(entity => new { Entity = entity, DataContract = pDataContractList.Single(dc => this.AreDCAndEntityEqual(dc, entity)) });

            foreach (var iToMerge in lToMerge)
            {
                this.MergeWithEntity(iToMerge.DataContract, iToMerge.Entity);
            }

            // add new
            List<TDC> newList = new List<TDC>();

            foreach (TDC dc in pDataContractList)
            {
                bool isnew = true;
                foreach (TEntity entity in pEntityList)
                {
                    if (isnew && AreDCAndEntityEqual(dc, entity))
                    {
                        isnew = false;
                    }
                }
                if (isnew)
                    newList.Add(dc);
            }

            foreach (TDC newDC in newList)
            {
                pEntityList.Add(MapToNewEntity(newDC));
            }

            //pEntityList.AddRange(
            //    pDataContractList
            //    .Where(dc => pEntityList
            //        .Count(entity => AreDCAndEntityEqual(dc, entity)) == 0)
            //    .Select(dc => MapToNewEntity(dc))); 
            
        }

        public virtual TDC MapToNewDC(TEntity pEntity)
        {
            return this.MergeWithDC(pEntity, CreateNewDC());
        }

        public virtual List<TDC> MapToNewDCs(IEnumerable<TEntity> pEntitys)
        {
            List<TDC> lResult = new List<TDC>();
	        if (pEntitys != null)
	        {
		        foreach (TEntity iEntity in pEntitys)
		        {
			        lResult.Add(this.MergeWithDC(iEntity, CreateNewDC()));
		        }
	        }
	        return lResult;
        }

        public virtual TDC CreateNewDC()
        {
            return new TDC();
        }

        public virtual List<TEntity> MapToNewEntities(IEnumerable<TDC> pDataContracts)
        {
            List<TEntity> lResult = new List<TEntity>();
	        if (pDataContracts != null)
	        {
		        foreach (TDC iDataContract in pDataContracts)
		        {
			        lResult.Add(this.MergeWithEntity(iDataContract, new TEntity()));
		        }
	        }
	        return lResult;
        }

        public virtual TEntity MapToNewEntity(TDC pDataContract)
        {
            return this.MergeWithEntity(pDataContract, new TEntity());
        }

        public abstract TDC MergeWithDC(TEntity pEntity, TDC pDataContract);

        public abstract TEntity MergeWithEntity(TDC pDataContract, TEntity pEntity);

        protected abstract bool AreDCAndEntityEqual(TDC pDC, TEntity pEntity);
    }
}