using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Threading;

using BeWo.Core.Service;
using BeWo.ServiceProxy;

using BS.Shared.DataContracts;
using BS.Shared.Extensions;
using BS.Shared.Services;
using BS.Shared.DataContracts.Compact;
using BS.Shared;

namespace BeWo.ViewModel
{
    public class AccountingBookingVM : AbstractBaseVM
    {
        public static string PropertyName_AccountingTransactionsForSelectedSupportConcept = "AccountingTransactionsForSelectedSupportConcept";

        public static string PropertyName_InformationString = "InformationString";

        public static string PropertyName_PrototypeVM = "PrototypeVM";

        public static string PropertyName_RecordCountInformationString = "RecordCountInformationString";

        private readonly BindingList<AccountingTransactionVM> _AccountingTransactionsForSelectedSupportConcept;

        private readonly Dictionary<SupportConceptCostBearerRelDC, bool> _AccountingTransactionsLoaded = new Dictionary<SupportConceptCostBearerRelDC, bool>();

        private BindingList<AccountingTransactionVM> _AccountingTransactionVMList;

        private string _InformationString;

        private AccountingTransactionVM _PrototypeVM;

        private string _RecordCountInformationString;

        private FlatSupportConceptTreeNodeDC _SelectedCustomerNode;

        public FlatSupportConceptTreeNodeDC SelectedCustomerNode { get { return _SelectedCustomerNode; } }

        private readonly Dictionary<long, SupportConceptTreeNodeDetailInfoDC> _Cb2Sc2BookingInfoDict = new Dictionary<long, SupportConceptTreeNodeDetailInfoDC>();

        public AccountingBookingVM(List<SupportConceptTreeNodeDC> pTree, List<ValueListEntryDC> categories)
        {
            this.SupportConceptTree = pTree.OrderBy(sc => sc.Customer.ToString()).ToList();
            this.Categories = categories.OrderBy(cat => cat.ToString()).ToList();

            this._AccountingTransactionsForSelectedSupportConcept = new BindingList<AccountingTransactionVM>();
        }

        public event EventHandler AccountingTransactionListChanged;

        public BindingList<AccountingTransactionVM> AccountingTransactionVMList
        {
            get { return this._AccountingTransactionVMList ?? (this._AccountingTransactionVMList = new BindingList<AccountingTransactionVM>()); }

            set { this._AccountingTransactionVMList = value; }
        }

        public BindingList<AccountingTransactionVM> AccountingTransactionsForSelectedSupportConcept
        {
            get { return this._AccountingTransactionsForSelectedSupportConcept; }
        }

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

        public List<ValueListEntryDC> Categories { get; private set; }

        public DispatcherObject DispatcherObject { get; set; }

        public bool EmployeeServiceRecordsFetched { get; set; }

        public string InformationString
        {
            get { return this._InformationString; }
        }

        public decimal OpenAmount { get; set; }

        public AccountingTransactionVM PrototypeVM
        {
            get
            {
                if (this._PrototypeVM == null)
                {
                    this._PrototypeVM = this.CreateVM(new AccountingTransactionDC());
                    this._PrototypeVM.Amount = null;
                    this._PrototypeVM.BookingDate = DateTime.Now;

                    // _PrototypeVM.PropertyChanged += (s, e) =>
                    // {
                    // if (e.PropertyName.Equals(ServiceRecordVM.PropertyName_Duration))
                    // FirePropertyChanged(PropertyName_Information);
                    // CheckCustomerAbsenceTimes();
                    // };
                }

                return this._PrototypeVM;
            }
        }

        public string RecordCountInformationString
        {
            get { return this._RecordCountInformationString; }
        }

        public AccountingTransactionVM SelectedVM { get; set; }

        public List<SupportConceptTreeNodeDC> SupportConceptTree { get; private set; }

        public bool WithoutClient { get; set; }

        #region Methods
        public void BuildAccountingTransactionsForSelectedTreeNode()
        {
            if (this.WithoutClient)
            {
                if (!this.EmployeeServiceRecordsFetched && this.DispatcherObject != null)
                {
                    ServiceFacade.DoOperationsServiceAsync(
                        s => s.GetUnassignedAccountingTransactions(),
                        r => this.DispatcherObject.Dispatch(
                            delegate
                            {
                                foreach (AccountingTransactionDC dc in r)
                                {
                                    this.AddAccountingTransactionToList(dc);
                                }

                                this.EmployeeServiceRecordsFetched = true;

                                // BuildServiceRecordsForTreeNode(_SelectedCustomerNode);
                                // if (!_CustomerServiceRecordsLoaded.ContainsKey(customer))
                                // _CustomerServiceRecordsLoaded.Add(customer, true);
                                this.BuildAccountingTransactionsForTreeNode(null);
                            }));
                }
                else
                {
                    this.BuildAccountingTransactionsForTreeNode(null);
                }
            }
            else
            {
                if (this._SelectedCustomerNode != null)
                {
                    if (this._SelectedCustomerNode.SupportConceptTreeNodeDC == null)
                    {
                        this.BuildAccountingTransactionsForTreeNode(this._SelectedCustomerNode);
                    }
                    else
                    {
                        SupportConceptCostBearerRelDC selectedRelDC = this._SelectedCustomerNode.SupportConceptTreeNodeDC.SupportConceptCostBearerRelDC;

                        if (selectedRelDC != null)
                        {
                            if (this._AccountingTransactionsLoaded.ContainsKey(selectedRelDC))
                            {
                                this.BuildAccountingTransactionsForTreeNode(this._SelectedCustomerNode);
                            }
                            else
                            {
                                if (this.DispatcherObject != null)
                                {
                                    ServiceFacade.DoOperationsServiceAsync(
                                        s => s.GetAccountingTransactions(null, null, selectedRelDC.CostBearer2SupportConceptOid),
                                        r => this.DispatcherObject.Dispatch(
                                            delegate
                                            {
                                                foreach (AccountingTransactionDC dc in r)
                                                {
                                                    this.AddAccountingTransactionToList(dc);
                                                }

                                                this.BuildAccountingTransactionsForTreeNode(this._SelectedCustomerNode);
                                                if (!this._AccountingTransactionsLoaded.ContainsKey(selectedRelDC))
                                                {
                                                    this._AccountingTransactionsLoaded.Add(selectedRelDC, true);
                                                }
                                            }));
                                }
                            }
                        }
                    }
                }
            }
        }

        public void ChangeTreeItemSelection(FlatSupportConceptTreeNodeDC lClickedNode)
        {
            if (this.WithoutClient || this._SelectedCustomerNode == null || lClickedNode.SupportConceptTreeNodeDC == null || this._SelectedCustomerNode.SupportConceptTreeNodeDC == null ||
                !this._SelectedCustomerNode.SupportConceptTreeNodeDC.Customer.EqualsNullCheck(lClickedNode.SupportConceptTreeNodeDC.Customer) ||
                !this._SelectedCustomerNode.SupportConceptTreeNodeDC.SupportConcept.EqualsNullCheck(lClickedNode.SupportConceptTreeNodeDC.SupportConcept) ||
                !this._SelectedCustomerNode.SupportConceptTreeNodeDC.CostBearer.EqualsNullCheck(lClickedNode.SupportConceptTreeNodeDC.CostBearer))
            {
                this._SelectedCustomerNode = lClickedNode;
                this.BuildAccountingTransactionsForSelectedTreeNode();
            }
        }

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

        public AccountingTransactionVM CreateFromPrototype()
        {
            AccountingTransactionVM lNewRecord = this.CreateVM();

            if (!this.WithoutClient)
            {
                lNewRecord.SupportConceptTreeNode = this._SelectedCustomerNode;
            }

            this.FirePropertyChanged(PropertyName_PrototypeVM);

            return lNewRecord;
        }

        public void RefreshAccountingTransactionsForSelectedTreeNode()
        {
            this.BuildAccountingTransactionsForSelectedTreeNode();
        }

        private void AddAccountingTransactionToList(AccountingTransactionDC dc)
        {
            bool contained = false;

            // List<ServiceRecordVM> contained = new List<ServiceRecordVM>();
            foreach (AccountingTransactionVM vm in this.AccountingTransactionVMList)
            {
                if (vm.DataContract.AccountingTransactionOid == dc.AccountingTransactionOid)
                {
                    contained = true;
                    break;
                }
            }

            if (!contained)
            {
                this.AccountingTransactionVMList.Add(this.CreateVM(dc));
            }
        }

        private void BuildAccountingTransactionsForTreeNode(FlatSupportConceptTreeNodeDC treeNode)
        {
            this._AccountingTransactionsForSelectedSupportConcept.Clear();
            this._InformationString = null;
            this._RecordCountInformationString = null;
            Func<AccountingTransactionVM, bool> f;
            this.OpenAmount = 0;
            if (this.WithoutClient || (treeNode != null && treeNode.SupportConceptTreeNodeDC != null))
            {
                decimal amountTotal = 0;
                decimal amountApprovedTotal = 0;
                decimal amountPerMonth = -1;
                decimal restAmount = 0;

                StringBuilder sb = new StringBuilder();

                if (this.WithoutClient)
                {
                    f = vm => vm.CostBearerRel == null && vm.SupportConcept == null && vm.CostBearer == null;
                }
                else
                {
                    var relDC = treeNode.SupportConceptTreeNodeDC.SupportConceptCostBearerRelDC;

                    SupportConceptTreeNodeDetailInfoDC infoDC = null;
                    long? supportConceptOid = null;
                    
                    if (treeNode.SupportConceptTreeNodeDC.SupportConcept != null)
                        supportConceptOid = treeNode.SupportConceptTreeNodeDC.SupportConcept.SupportConceptOid;

                    CompactCustomerDC customer = treeNode.SupportConceptTreeNodeDC.Customer;

                    long? cb2ScOid = relDC.CostBearer2SupportConceptOid;

                    if (customer != null && cb2ScOid.HasValue && !this._Cb2Sc2BookingInfoDict.ContainsKey(cb2ScOid.Value))
                    {

                        infoDC = ServiceFacade.DoOperationsServiceSync(s => s.GetSupportConceptTreeNodeDetailInfo(customer.CustomerOid, null, cb2ScOid));

                        if (relDC.CostBearer != null && (relDC.CostBearer.CostRatePeriods == null || relDC.CostBearer.CostRatePeriods.Count == 0))
                        {
                            relDC.CostBearer = ServiceFacade.DoOperationsServiceSync(s => s.FillCostbearerDetails(relDC.CostBearer));
                        }

                        this._Cb2Sc2BookingInfoDict.Add(cb2ScOid.Value, infoDC);
                    }
                    if (infoDC == null && cb2ScOid.HasValue && this._Cb2Sc2BookingInfoDict.ContainsKey(cb2ScOid.Value))
                    {
                        infoDC = this._Cb2Sc2BookingInfoDict[cb2ScOid.Value];
                    }
                    if (infoDC != null)
                    {
                        relDC.ApprovalPeriodList = infoDC.ApprovalPeriods;
                    }

                    var calc = Calculations.GetInstance(relDC.CostBearer.CostBearerID);

                    
                    amountPerMonth = calc.GetApprovedAmountPerMonth(relDC, DateTime.Now) ?? 0m;
                    amountApprovedTotal = calc.GetApprovedAmountTotal(relDC) ?? 0m;

                    if (treeNode.SupportConceptTreeNodeDC.CostBearer != null)
                    {
                        f =
                            vm =>
                            {
                                return vm.CostBearerRel != null && vm.CostBearerRel.SupportConcept != null && vm.CostBearerRel.SupportConcept.Customer != null &&
                                       treeNode.SupportConceptTreeNodeDC.Customer.Equals(vm.CostBearerRel.SupportConcept.Customer) &&
                                       treeNode.SupportConceptTreeNodeDC.SupportConcept.Equals(vm.CostBearerRel.SupportConcept) &&
                                       treeNode.SupportConceptTreeNodeDC.CostBearer.Equals(vm.CostBearerRel.CostBearer);
                            };
                    }
                    else if (treeNode.SupportConceptTreeNodeDC.SupportConcept != null)
                    {
                        f =
                            vm =>
                            {
                                return vm.CostBearerRel != null && vm.CostBearerRel.SupportConcept != null && vm.CostBearerRel.SupportConcept.Customer != null &&
                                       treeNode.SupportConceptTreeNodeDC.Customer.Equals(vm.CostBearerRel.SupportConcept.Customer) &&
                                       treeNode.SupportConceptTreeNodeDC.SupportConcept.Equals(vm.CostBearerRel.SupportConcept);
                            };
                    }
                    else
                    {
                        f =
                            vm =>
                            {
                                return vm.CostBearerRel != null && vm.CostBearerRel.SupportConcept != null && vm.CostBearerRel.SupportConcept.Customer != null &&
                                       treeNode.SupportConceptTreeNodeDC.Customer.Equals(vm.CostBearerRel.SupportConcept.Customer);
                            };
                    }
                }

                foreach (AccountingTransactionVM vm in this.AccountingTransactionVMList.Where(f))
                {
                    amountTotal += vm.Amount.Value;

                    this._AccountingTransactionsForSelectedSupportConcept.Add(vm);
                }

                sb.Append("Bereits gezahlt: ");
                sb.Append(amountTotal.ToString("N"));

                restAmount = amountApprovedTotal - amountTotal;

                if (amountPerMonth >= 0)
                {
                    sb.Append("\nInsgesamt zu zahlender Betrag: ");
                    sb.Append(amountApprovedTotal.ToString("N"));
                }

                if (amountPerMonth >= 0)
                {
                    sb.Append(", monatlicher Abschlag : ");
                    sb.Append(amountPerMonth.ToString("N"));
                }

                sb.Append("\nNoch offen: ");
                sb.Append(restAmount.ToString("N"));

                this._InformationString = sb.ToString();
                this._RecordCountInformationString = "anz Einträge: " + this._AccountingTransactionsForSelectedSupportConcept.Count;

                this.OpenAmount = restAmount;
            }

            this.FirePropertyChanged(PropertyName_AccountingTransactionsForSelectedSupportConcept);
            this.FirePropertyChanged(PropertyName_InformationString);
            this.FirePropertyChanged(PropertyName_RecordCountInformationString);

            if (this.AccountingTransactionListChanged != null)
            {
                this.AccountingTransactionListChanged(this, new EventArgs());
            }
        }

        private AccountingTransactionVM CreateVM(AccountingTransactionDC pDC)
        {
            return new AccountingTransactionVM(pDC);
        }

        private AccountingTransactionVM CreateVM()
        {
            AccountingTransactionVM lNewRecord = this._PrototypeVM.Clone();
            lNewRecord.BookingDate = this._PrototypeVM.BookingDate;
            lNewRecord.Amount = this._PrototypeVM.Amount;
            lNewRecord.Category = this._PrototypeVM.Category;
            lNewRecord.Notice = this._PrototypeVM.Notice;
            lNewRecord.SetInsertedOn(DateTime.Now);
            lNewRecord.InsUser = BeWoApp.LoggedOnUser.Employee.FirstName + " " + BeWoApp.LoggedOnUser.Employee.LastName;

            return lNewRecord;
        }
        #endregion
    }
}