using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Xml;
using DevExpress.XtraScheduler;
using DevExpress.Xpf.Scheduler;

using BS.Shared;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;

using BeWo.ViewModel.ListViewModel;
using BeWo.ServiceProxy;
using BeWo.Scheduler.View;
using BeWo.Scheduler.Resources;


namespace BeWo.Scheduler.ViewModel
{
    public class AppointmentListVM : AbstractDCListMapperVM<AppointmentDC, AppointmentVM>, ISchedulerViewModel
    {
        private List<BeWoAppointmentCategory> mAppointmentCategoryList;
        private IList<IBeWoAppointmentResource> mAppointmentResourceList;
        private readonly List<CompactCustomerDC> mAllCustomers;
        private readonly List<CompactEmployeeDC> mAllEmployees;
        private readonly List<AppointmentCategoryDC> mAllCategories;
        private BindingList<IBeWoAppointment> mAppointmentList;
        private readonly long? mCustomerOid;
	    private readonly List<CompactSupportConceptDC> mSupportConcepts;

        public AppointmentListVM(long? customerOid, List<AppointmentCategoryDC> allCats, List<CompactCustomerDC> allCustomers, List<CompactEmployeeDC> allemps, IEnumerable<AppointmentDC> appList)
            : base(appList)
        {
            mCustomerOid = customerOid;
            mAllCustomers = allCustomers;
            mAllEmployees = allemps;
            mAllCategories = allCats;

            InitViewModel();
			
			InitChangedOccurrencyCustomFields(appList.Where(t => t.Type == 3).ToList());
        }

		public AppointmentListVM(long? customerOid, List<AppointmentCategoryDC> allCats, List<CompactCustomerDC> allCustomers, List<CompactEmployeeDC> allemps, IEnumerable<AppointmentDC> appList, List<CompactSupportConceptDC> allSupportConcepts)
			: base(appList)
		{
			mCustomerOid = customerOid;
			mAllCustomers = allCustomers;
			mAllEmployees = allemps;
			mAllCategories = allCats;
			mSupportConcepts = allSupportConcepts;

			InitViewModel();
			
			InitChangedOccurrencyCustomFields(appList.Where(t => t.Type == 3).ToList());
		}

        private void InitViewModel()
        {
			// Geburtstage und Abwesenheiten hinzufügen
			foreach (var klient in mAllCustomers.Where(c => !c.IsArchived))
			{
				if (klient.DateOfBirth.HasValue)
				{
					var geburtstag = new CommonCalendarVM(klient, null, null);
					geburtstag.CustomFields["Readonly"] = true;
					geburtstag.CustomFields["CustomerOid"] = klient;
					Appointments.Add(geburtstag);
				}

				if (klient.AbsenceTimes == null) continue;

				foreach (var abwesenheit in klient.AbsenceTimes)
				{
					var abw = new CommonCalendarVM(klient, null, abwesenheit);
					abw.CustomFields["Readonly"] = true;
					abw.CustomFields["CustomerOid"] = klient;
					Appointments.Add(abw);
				}
			}

	        foreach (var sc in mSupportConcepts)
	        {
		        var hp = new CommonCalendarVM(sc.Customer, sc, null);
				hp.CustomFields["Readonly"] = true;
				hp.CustomFields["CustomerOid"] = sc.Customer;
				Appointments.Add(hp);
	        }

            mAppointmentCategoryList = new List<BeWoAppointmentCategory>();
            mAppointmentResourceList = new List<IBeWoAppointmentResource>();

            var oid2Category = new Dictionary<long, BeWoAppointmentCategory>();
            var catOid2Index = new Dictionary<long, int>();

            if (mAllCustomers != null)
            {
                foreach (CompactCustomerDC item in mAllCustomers)
                {
                    IBeWoAppointmentResource res = new CustomerResource(item);
                    mAppointmentResourceList.Add(res);
                }
            }

            int index = 0;
            foreach (AppointmentCategoryDC item in mAllCategories)
            {
                var cat = new BeWoAppointmentCategory
                {
                    CategoryObject = item,
                    Color = ((SolidColorBrush) new BrushConverter().ConvertFrom(item.Color)).Color,
                    DisplayName = item.DisplayName,
                    MenuCaption = item.DisplayName
                };
                if (!oid2Category.ContainsKey(item.AppointmentCategoryOid.Value))
                    oid2Category.Add(item.AppointmentCategoryOid.Value, cat);

                mAppointmentCategoryList.Add(cat);

                if (!catOid2Index.ContainsKey(item.AppointmentCategoryOid.Value))
                    catOid2Index.Add(item.AppointmentCategoryOid.Value, index);

                index++;
            }

            foreach (var item in VMList)
            {   
				item.ResourceId = item.DataContract.CustomerOid;
                item.CustomFields.Add("EmployeeOid", item.EmployeeOid);
				item.CustomFields.Add("CustomerOid", item.DataContract.CustomerOid);

                if (item.AppointmentCategoryDC != null && oid2Category.ContainsKey(item.AppointmentCategoryDC.AppointmentCategoryOid.Value))
                    item.AppointmentCategory = oid2Category[item.AppointmentCategoryDC.AppointmentCategoryOid.Value];

                if (item.AppointmentCategoryDC != null && catOid2Index.ContainsKey(item.AppointmentCategoryDC.AppointmentCategoryOid.Value))
                    item.LabelId = catOid2Index[item.AppointmentCategoryDC.AppointmentCategoryOid.Value];
            }

			Categories.Add(new BeWoAppointmentCategory
			{
				CategoryObject = new object(),
				Color = Color.FromRgb(153, 59, 59),
				DisplayName = "Hilfeplan",
				MenuCaption = "Hilfeplan"
			});
			Categories.Add(new BeWoAppointmentCategory
			{
				CategoryObject = new object(),
				Color = Color.FromRgb(59, 119, 153),
				DisplayName = "Geburtstag",
				MenuCaption = "Geburtstag"
			});
			Categories.Add(new BeWoAppointmentCategory
			{
				CategoryObject = new object(),
				Color = Color.FromRgb(119, 221, 119),
				DisplayName = "Abwesenheit",
				MenuCaption = "Abwesenheit"
			});

			foreach (var item in Appointments.Where(a => a.CustomFields.ContainsKey("Readonly") && a.CustomFields["Readonly"] != null))
			{
				var xyp = item as CommonCalendarVM;
				if (xyp == null) continue;
				switch (xyp.Typ)
				{
					case CommonCalendarVMType.Geburtstag:
						xyp.LabelId = Categories.IndexOf(Categories.First(f => f.Color.Equals(Color.FromRgb(59, 119, 153))));
						break;
					case CommonCalendarVMType.Abwesenheit:
						xyp.LabelId = Categories.IndexOf(Categories.First(f => f.Color.Equals(Color.FromRgb(119, 221, 119))));
						break;
					case CommonCalendarVMType.Hilfeplan:
						xyp.LabelId = Categories.IndexOf(Categories.First(f => f.Color.Equals(Color.FromRgb(153, 59, 59))));
						break;
				}
			}

            ((List<IBeWoAppointmentResource>)mAppointmentResourceList).Sort((a, b) => a.Caption.CompareTo(b.Caption));
        }

        public IList<BeWoAppointmentCategory> Categories
        {
            get { return mAppointmentCategoryList; }
        }
		public IList<IBeWoAppointmentResource> Resources
        {
            get { return mAppointmentResourceList; }
        }

		public Dictionary<Guid, Dictionary<string, Dictionary<string, object>>> ChangedOccurencyCustomFields
		{
			get { return changedOccurencyCustomFields; }
			private set { changedOccurencyCustomFields = value; }
		}

		private Dictionary<Guid, Dictionary<string, Dictionary<string, object>>> changedOccurencyCustomFields;

	    public object SelectedFilterObject { get; set; }

	    public BindingList<IBeWoAppointment> Appointments
        {
            get
            {
                if (mAppointmentList == null)
                {
                    mAppointmentList = new BindingList<IBeWoAppointment>(VMList.ToList<IBeWoAppointment>());
                    mAppointmentList.AddingNew += mAppointmentList_AddingNew;
                }

                return mAppointmentList;
            }
        }

        void mAppointmentList_AddingNew(object sender, AddingNewEventArgs e)
        {
            var dc = new AppointmentDC
            {
                CustomerOid = mCustomerOid,
                EmployeeOid = BeWoApp.LoggedOnEmployee.EmployeeOid,
                Status = 0
            };

            e.NewObject = new AppointmentVM(dc);
        }

        public SchedulerSettings GetDefaultSchedulerSettings()
        {
            var settings = new SchedulerSettings(AppointmentKind.Customer, AppointmentViewType.WorkWeek);

            return settings;
        }

		public void SaveAppointments(SchedulerControl control, IEnumerable<Appointment> list)
        {
			var sindGleich = false;
            var dcList = new List<AppointmentDC>();
            foreach (var item in list)
            {
                var vm = item.GetSourceObject(control.GetCoreStorage()) as AppointmentVM;
	            if (vm == null) continue;

	            UpdateAppointment(item, vm);

	            if (item.Type.Equals(AppointmentType.ChangedOccurrence))
		            sindGleich = TermineSindGleich(geoeffneterTermin, item);
	            if (!sindGleich)
		            dcList.Add(vm.CommitToDataContract());
            }
			if (dcList.Count <= 0) return;

			var erg = new List<AppointmentDC>();
			ServiceFacade.DoCustomerServiceSync(s => erg = s.UpdateAppointments(dcList));
			UpdateViewModel(erg);
        }

		private void UpdateAppointment(Appointment app, AppointmentVM vm)
		{
			if (app.RecurrenceInfo != null)
			{
				var index = app.RecurrenceIndex.ToString();
				var id = app.RecurrenceInfo.Id;

				if (changedOccurencyCustomFields.ContainsKey(app.RecurrenceInfo.Id) && changedOccurencyCustomFields[id].ContainsKey(index))
				{
					if (app.Type.Equals(AppointmentType.ChangedOccurrence))
					{
						app.CustomFields["EmployeeOid"] = changedOccurencyCustomFields[id][index]["EmployeeOid"];
						app.CustomFields["CustomerOid"] = changedOccurencyCustomFields[id][index]["CustomerOid"];
					}
				}
			}

			//Update Resource (Customer)
			long? oid = null;

			if (vm.ResourceId != null)
				oid = (long)vm.ResourceId;

			vm.CustomerOid = (long?)app.CustomFields["CustomerOid"];

			//Update Category
			AppointmentCategoryDC cat = null;
			if (mAppointmentCategoryList != null)
			{
				if (vm.LabelId >= 0 && vm.LabelId < mAppointmentCategoryList.Count)
				{
					BeWoAppointmentCategory bcat = mAppointmentCategoryList[vm.LabelId];
					cat = bcat.CategoryObject as AppointmentCategoryDC;

					vm.AppointmentCategory = bcat;
				}
			}

			vm.AppointmentCategoryDC = cat;

			//Update Employee
			var empOid = (long?)app.CustomFields["EmployeeOid"];
			vm.EmployeeOid = empOid;
		}

		public void ApplyChangesToChangedOccurencyCustomFields(long employeeOid, long customerOid, string index, Guid id)
		{
			if (!changedOccurencyCustomFields.ContainsKey(id) || !changedOccurencyCustomFields[id].ContainsKey(index)) return;

			changedOccurencyCustomFields[id][index]["EmployeeOid"] = employeeOid;
			changedOccurencyCustomFields[id][index]["CustomerOid"] = customerOid;
		}

		private void UpdateViewModel(IEnumerable<AppointmentDC> updatedDcList)
		{
			foreach (var dc in updatedDcList)
			{
				AppointmentVM found = null;
				foreach (var ivm in mAppointmentList)
				{
					var vm = ivm as AppointmentVM;
					if (vm != null && vm.DataContract.Equals(dc))
						found = vm;
				}

				if (found != null)
					found.DataContract = dc;
			}
		}

        public void DeleteAppointments(SchedulerControl control, IEnumerable<Appointment> list)
        {
            var dcList = list.Select(item => item.GetSourceObject(control.GetCoreStorage())).OfType<AppointmentVM>().Select(vm => vm.CommitToDataContract()).Where(dc => dc.AppointmentOid.HasValue).ToList();
			var idList = (from item in dcList where item.AppointmentOid.HasValue select item.AppointmentOid.Value).ToList();
			
			ServiceFacade.DoCustomerServiceSync(s => dcList = s.GetAppointmentsById(idList));

            if (dcList.Count > 0)
                ServiceFacade.DoCustomerServiceAsync(
                    s => s.DeactivateAppointments(dcList.ToDictionary(dc => dc.AppointmentOid != null ? dc.AppointmentOid.Value : 0, 
                                                                      dc => dc.AppointmentVersion != null ? dc.AppointmentVersion.Value : 0)));
        }

        public void InsertNewAppointments(SchedulerControl control, IEnumerable<Appointment> list)
        {
            var dcList = new List<AppointmentDC>();
            foreach (var item in list)
            {
                var vm = item.GetSourceObject(control.GetCoreStorage()) as AppointmentVM;
	            if (vm == null) continue;

	            UpdateAppointment(item, vm);
	            dcList.Add(vm.CommitToDataContract());
            }
            if (dcList.Count > 0)
            {
				var liste = new List<AppointmentDC>();
				ServiceFacade.DoCustomerServiceSync(s => liste = s.InsertNewAppointments(dcList));
				UpdateViewModel(liste);
            }
        }

        public void AddCustomFieldsMapping(SchedulerStorage schedulerStorage)
        {
            var employeeMapping = new SchedulerCustomFieldMapping("EmployeeOid", "EmployeeOid");
			var customerMapping = new SchedulerCustomFieldMapping("CustomerOid", "CustomerOid");
	        var readonlyMapping = new SchedulerCustomFieldMapping("Readonly", "Readonly");

            schedulerStorage.AppointmentStorage.CustomFieldMappings.Add(employeeMapping);
			schedulerStorage.AppointmentStorage.CustomFieldMappings.Add(customerMapping);
	        schedulerStorage.AppointmentStorage.CustomFieldMappings.Add(readonlyMapping);
        }

        public void InitNewAppointment(Appointment appointment)
        {
            //appointment.ResourceId = mCustomerOid;
            appointment.CustomFields["EmployeeOid"] = BeWoApp.LoggedOnEmployee.EmployeeOid;
	        appointment.CustomFields["CustomerOid"] = mCustomerOid;
			appointment.CustomFields["Readonly"] = null;
            appointment.StatusId = 0;
        }

		private Appointment geoeffneterTermin;
        AbstractAppointmentEditForm ISchedulerViewModel.GetEditAppointmentForm(SchedulerControl control, Appointment appointment)
        {
	        if (appointment.CustomFields["Readonly"] != null)
	        {
		        return new CommonCalendarEditForm(control, appointment);
	        }

			geoeffneterTermin = control.Storage.CreateAppointment(AppointmentType.Normal);
	        geoeffneterTermin.Subject = appointment.Subject;
	        geoeffneterTermin.LabelId = appointment.LabelId;
			geoeffneterTermin.AllDay = appointment.AllDay;
			geoeffneterTermin.Start = appointment.Start;
			geoeffneterTermin.End = appointment.End;
			geoeffneterTermin.Description = appointment.Description;
			geoeffneterTermin.CustomFields["EmployeeOid"] = appointment.CustomFields["EmployeeOid"];
			geoeffneterTermin.CustomFields["CustomerOid"] = appointment.CustomFields["CustomerOid"];

			if (!appointment.Type.Equals(AppointmentType.ChangedOccurrence))
				return new CustomerAppointmentEditForm(this, control, appointment, mAllCustomers, mAllEmployees, SelectedFilterObject);

			var index = appointment.RecurrenceIndex;
			var id = appointment.RecurrenceInfo.Id;

			if (changedOccurencyCustomFields.ContainsKey(id) && changedOccurencyCustomFields[id].ContainsKey(index.ToString()))
			{
				var specialCustomFields = changedOccurencyCustomFields[id][index.ToString()];
				appointment.CustomFields["EmployeeOid"] = specialCustomFields["EmployeeOid"];
				geoeffneterTermin.CustomFields["EmployeeOid"] = specialCustomFields["EmployeeOid"];
				appointment.CustomFields["CustomerOid"] = specialCustomFields["CustomerOid"];
				geoeffneterTermin.CustomFields["CustomerOid"] = specialCustomFields["CustomerOid"];
			}

			return new CustomerAppointmentEditForm(this, control, appointment, mAllCustomers, mAllEmployees, SelectedFilterObject);
        }

		public bool FilterAppointment(string filterCategory, object selectedObject, Appointment appointment, TimeInterval interval)
		{
			long? x;

			if (appointment.CustomFields["Readonly"] != null && ((bool) appointment.CustomFields["Readonly"]))
			{
				x = ((CompactCustomerDC) appointment.CustomFields["CustomerOid"]).CustomerOid;
			}
			else
			{
				if (appointment.CustomFields["CustomerOid"] != null)
					x = (long) appointment.CustomFields["CustomerOid"];
				else
					x = (long) appointment.ResourceId;
			}

	        if (selectedObject == null || x == null) return true;

			SelectedFilterObject = selectedObject;

	        return !x.Equals(((CompactCustomerDC) selectedObject).CustomerOid);
        }

		private static bool TermineSindGleich(Appointment eins, Appointment zwei)
		{   // Die Appointments sind irgendwie nie gleich...
			if (eins == null || zwei == null) return false;

			return eins.Description == zwei.Description &&
				   eins.AllDay == zwei.AllDay &&
				   eins.Subject == zwei.Subject &&
				   eins.LabelId == zwei.LabelId &&
				   eins.Start == zwei.Start &&
				   eins.End == zwei.End &&
				   eins.CustomFields["EmployeeOid"] == zwei.CustomFields["EmployeeOid"] &&
				   eins.CustomFields["CustomerOid"] == zwei.CustomFields["CustomerOid"];

		}

	    public void InitChangedOccurrencyCustomFields<T>(IEnumerable<T> appList) where T : IDataContract
	    {
			var liste = (List<AppointmentDC>)appList;

			changedOccurencyCustomFields = new Dictionary<Guid, Dictionary<string, Dictionary<string, object>>>();
			foreach (var termin in liste)
			{
				var ri = new RecurrenceInfo();
				ri.FromXml(termin.RecurrenceInfo);
				var index = string.Empty;
				using (var reader = XmlReader.Create(new StringReader(termin.RecurrenceInfo)))
				{
					reader.ReadToFollowing("RecurrenceInfo");
					reader.MoveToAttribute("Index");
					index = reader.Value;
				}

				if (index.Equals(""))
					index = "0";

				var customFieldCollection = new Dictionary<string, object>();
				customFieldCollection["EmployeeOid"] = termin.EmployeeOid;
				customFieldCollection["CustomerOid"] = termin.CustomerOid;

				if (!changedOccurencyCustomFields.ContainsKey(ri.Id))
					changedOccurencyCustomFields.Add(ri.Id, new Dictionary<string, Dictionary<string, object>> { { index, customFieldCollection } });
				else if (!changedOccurencyCustomFields[ri.Id].ContainsKey(index))
					changedOccurencyCustomFields[ri.Id].Add(index, customFieldCollection);
				else
					changedOccurencyCustomFields[ri.Id][index] = customFieldCollection;
			}
	    }
    }
}