using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

using BS.Shared.Extensions;

namespace BeWo.ViewModel.ListViewModel
{
    public abstract class AbstractDCListMapperVM<DCType, VMType> : AbstractBaseVM where VMType : AbstractDCMapperVM<DCType> where DCType : new()
    {
        #region Constants and Fields

        public static string PropertyName_EditVM = "EditVM";

        public static string PropertyName_NewVM = "NewVM";

        private readonly ObservableCollection<DCType> _DCBackup;

        private VMType _EditVM;

        private VMType _NewVM;

        private BindingList<VMType> _VMList;

        #endregion

        #region Constructors and Destructors

        public AbstractDCListMapperVM(IEnumerable<DCType> pDCList)
        {
            _DCBackup = pDCList != null ? pDCList.ToObservableCollection() : new ObservableCollection<DCType>();

			_DCBackup.CollectionChanged += (s, e) => _VMList.AddRange(e.NewItems.Cast<DCType>().Select(CreateVM));
        }

        public AbstractDCListMapperVM() : this(null) {}

        #endregion

        #region Properties

        public virtual int AddedCount
        {
            get
            {
                return this.VMList.Where(vm => vm.IsNew).Count();
            }
        }

        public virtual int ChangedCount
        {
            get
            {
                return this.VMList.Where(vm => vm.IsDirty && !vm.IsNew).Count();
            }
        }

        public ObservableCollection<DCType> DCBackup
        {
            get
            {
                return this._DCBackup;
            }
        }

        public VMType EditVM
        {
            get
            {
                return this._EditVM;
            }

            set
            {
                this._EditVM = value;
                this.FirePropertyChanged(PropertyName_EditVM);
            }
        }

        public override bool IsDirty
        {
            get
            {
                return ChangedCount != 0 || RemovedCount != 0 || AddedCount != 0;
            }
        }

        public VMType NewVM
        {
            get
            {
                return this._NewVM ?? (this.NewVM = this.CreateVM(new DCType()));
            }

            set
            {
                this._NewVM = value;
                this.FirePropertyChanged(PropertyName_NewVM);
            }
        }

        public virtual int RemovedCount
        {
            get
            {
                return this.GetRemoved().Count;
            }
        }

        public BindingList<VMType> VMList
        {
	        get
	        {
		        if (_VMList == null)
		        {
			        _VMList = new BindingList<VMType>(_DCBackup.Select(CreateVM).ToList()) {AllowNew = true};
			        _VMList.AddingNew += (s, e) => e.NewObject = CreateVM(new DCType());
		        }

		        return _VMList;
	        }

            set
            {
                _VMList = value;
            }
        }

        protected virtual Comparison<VMType> Comparison
        {
            get
            {
                return null;
            }
        }

        #endregion

        #region Public Methods

        public VMType AddNewVMToList()
        {
            this.VMList.Add(this.NewVM);

            // VMList.Sort();
            VMType lVM = this.NewVM;
            this.NewVM = null;

            // FirePropertyChanged("VMList");
            return lVM;
        }

        public VMType AddNewVMToList(bool pSetAsEdit)
        {
            this._VMList.Add(this._NewVM);

            // _VMList.Sort();
            if (pSetAsEdit)
            {
                this.EditVM = this._NewVM;
            }

            this.NewVM = null;
            return this.EditVM;
        }

        public virtual List<DCType> CommitAdded()
        {
            return this.VMList.Where(vm => vm.IsNew).Select(vm => vm.CommitToDataContract()).ToList();
        }

        public virtual List<DCType> CommitChanged()
        {
            return this.VMList.Where(vm => vm.IsDirty && !vm.IsNew).Select(vm => vm.CommitToDataContract()).ToList();
        }

        public virtual List<DCType> CopyToDCList(bool doCommit)
        {
            if (doCommit && this._VMList != null)
            {
                return doCommit
                           ? this._VMList.Select(vm => vm.CommitToDataContract()).ToList()
                           : this._VMList.Select(i => i.CopyToEmptyDataContract()).ToList();
            }

            return null;
        }

        public void DeleteEditVM()
        {
            this._VMList.Remove(this._EditVM);
            this.EditVM = null;
        }

        public virtual List<DCType> GetRemoved()
        {
            return this._DCBackup.Where(dc => !this._VMList.Select(vm => vm.DataContract).Contains(dc)).ToList();
        }

        #endregion

        #region Methods

        protected virtual VMType CreateVM(DCType pDC)
        {
            var lConstr = typeof(VMType).GetConstructor(new[] { typeof(DCType) });

            return lConstr.Invoke(new object[] { pDC }) as VMType;
        }

        #endregion
    }
}