using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Media;
using System.Xml;

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

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

using DevExpress.Xpf.Scheduler;
using DevExpress.XtraScheduler;

namespace BeWo.Scheduler.ViewModel
{
	public class ResourceAppointmentListVM : AbstractDCListMapperVM<ResourceAppointmentDC, ResourceAppointmentVM>, ISchedulerViewModel
	{
		private IList<BeWoAppointmentCategory> mAppointmentCategoryList;
		private IList<IBeWoAppointmentResource> mAppointmentResourceList;
		public IList<BeWoAppointmentCategory> Categories { get; private set; }
		public IList<IBeWoAppointmentResource> Resources { get; private set; }

		public Dictionary<Guid, Dictionary<string, Dictionary<string, object>>> ChangedOccurencyCustomFields
		{
			get { return changedOccurencyCustomFields; } 
			private set { changedOccurencyCustomFields = value; }
		}
		public object SelectedFilterObject { get; set; }
		private readonly Dictionary<ValueListEntryDC, List<ResourceDC>> mCategories2Resources;
		private readonly List<CompactEmployeeDC> mAllEmployees;
		private BindingList<IBeWoAppointment> mAppointmentList;
		public BindingList<IBeWoAppointment> Appointments
		{
			get
			{
				if (mAppointmentList == null)
				{
					mAppointmentList = new BindingList<IBeWoAppointment>(VMList.ToList<IBeWoAppointment>());
					mAppointmentList.AddingNew += mAppointmentList_AddingNew;
				}
				
				return mAppointmentList;
			}
		}

		private Dictionary<long, BeWoAppointmentCategory> oid2Category;
		private Dictionary<long, int> catOid2Index;
		private Dictionary<Guid, Dictionary<string, Dictionary<string, object>>> changedOccurencyCustomFields;
		
		private static void mAppointmentList_AddingNew(object sender, AddingNewEventArgs e)
		{
			var dc = new ResourceAppointmentDC {Status = 0};
			ServiceFacade.DoEmployeeServiceSync(a => dc.Employee = a.LoadCompactEmployee(BeWoApp.LoggedOnEmployee.EmployeeOid.Value));

			e.NewObject = new ResourceAppointmentVM(dc);
		}

		public ResourceAppointmentListVM(Dictionary<ValueListEntryDC, List<ResourceDC>> lCategoriesToResources, List<CompactEmployeeDC> allemps, IEnumerable<ResourceAppointmentDC> appList)
			: base(appList)
		{
			mCategories2Resources = lCategoriesToResources;
			mAllEmployees = allemps;
			
			InitViewModel();

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

		private void InitViewModel()
		{
			oid2Category = new Dictionary<long, BeWoAppointmentCategory>();
			catOid2Index = new Dictionary<long, int>();

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

			var index = 0;
			foreach (var category in mCategories2Resources.Keys)
			{
				foreach (var item in mCategories2Resources[category])
				{
					var solidColorBrush = (SolidColorBrush)new BrushConverter().ConvertFrom(item.Color);
					var color = solidColorBrush == null ? Color.FromArgb(255, 4, 180, 208) : solidColorBrush.Color;

					var cat = new BeWoAppointmentCategory
					{
						CategoryObject = item,
						Color = color,
						DisplayName = item.Name,
						MenuCaption = item.Name
					};
					
					if (item.ResourceOid.HasValue && !oid2Category.ContainsKey(item.ResourceOid.Value))
						oid2Category.Add(item.ResourceOid.Value, cat);

					mAppointmentCategoryList.Add(cat);

					if (item.ResourceOid.HasValue && !catOid2Index.ContainsKey(item.ResourceOid.Value))
						catOid2Index.Add(item.ResourceOid.Value, index);

					index++;
				}
			}

			Categories = mAppointmentCategoryList;

			foreach (var item in VMList)
			{
				if (item.Resource != null && item.Resource.ResourceOid != null)
				{
					item.CustomFields.Add("BeWoResourceOid", item.Resource.ResourceOid.Value);
					item.CustomFields.Add("Employee", item.Employee);

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

					item.LabelId = catOid2Index[item.Resource.ResourceOid.Value];
				}
			}

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

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

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

				UpdateResourceAppointment(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<ResourceAppointmentDC>();
			ServiceFacade.DoResourceServiceSync(r => erg = r.UpdateResourceAppointments(dcList));
			UpdateViewModel(erg);
		}

		private void UpdateResourceAppointment(Appointment app, ResourceAppointmentVM vm)
		{
			vm.Employee = app.CustomFields["Employee"] as CompactEmployeeDC;
			if (mAppointmentCategoryList == null) return;

			foreach (var category in mAppointmentCategoryList)
			{
				var co = category.CategoryObject as ResourceDC;

				if (co == null) continue;
				if (!co.ResourceOid.HasValue || !co.ResourceOid.Equals(app.CustomFields["BeWoResourceOid"])) continue;

				vm.AppointmentCategory = category;
				break;
			}

			app.LabelId = catOid2Index.First(kvp => kvp.Key.Equals(((ResourceDC) vm.AppointmentCategory.CategoryObject).ResourceOid.Value)).Value;

			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["Employee"] = changedOccurencyCustomFields[id][index]["Employee"];
						app.CustomFields["BeWoResourceOid"] = changedOccurencyCustomFields[id][index]["BeWoResourceOid"];
					}
				}
			}

			if (vm.AppointmentCategory != null && vm.AppointmentCategory.CategoryObject is ResourceDC)
				vm.Resource = vm.AppointmentCategory.CategoryObject as ResourceDC;
		}

		public void ApplyChangesToChangedOccurencyCustomFields(CompactEmployeeDC employee, long? beWoResourceOid, string index, Guid id)
		{
			if (!changedOccurencyCustomFields.ContainsKey(id) || !changedOccurencyCustomFields[id].ContainsKey(index)) return;

			changedOccurencyCustomFields[id][index]["Employee"] = employee;
			changedOccurencyCustomFields[id][index]["BeWoResourceOid"] = beWoResourceOid;
		}

		private void UpdateViewModel(IEnumerable<ResourceAppointmentDC> updatedDCList)
		{
			foreach (var dc in updatedDCList)
			{
				ResourceAppointmentVM found = null;
				foreach (var ivm in Appointments)
				{
					var vm = ivm as ResourceAppointmentVM;
					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<ResourceAppointmentVM>().Select(vm => vm.CommitToDataContract()).Where(dc => dc.ResourceAppointmentOid.HasValue).ToList();
			var idList = (from item in dcList where item.ResourceAppointmentOid.HasValue select item.ResourceAppointmentOid.Value).ToList();
			ServiceFacade.DoResourceServiceSync(s=> dcList = s.GetResourceAppointmentsById(idList));

			if (dcList.Count <= 0) return;

			var wörterbuch = dcList.ToDictionary(dc => dc.ResourceAppointmentOid != null ? dc.ResourceAppointmentOid.Value : 0, 
				dc => dc.ResourceAppointmentVersion != null ? dc.ResourceAppointmentVersion.Value : 0);

			ServiceFacade.DoResourceServiceAsync(s => s.DeleteResourceAppointments(wörterbuch));
		}

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

				UpdateResourceAppointment(item, vm);
				dcList.Add(vm.CommitToDataContract());
			}
			if (dcList.Count > 0)
			{
				var liste = new List<ResourceAppointmentDC>();
				ServiceFacade.DoResourceServiceSync(r => liste = r.InsertNewResourceAppointments(dcList));
				UpdateViewModel(liste);
			}
		}

		private Appointment geoeffneterTermin;
		AbstractAppointmentEditForm ISchedulerViewModel.GetEditAppointmentForm(SchedulerControl control, Appointment appointment)
		{
			geoeffneterTermin = control.Storage.CreateAppointment(AppointmentType.Normal);
			geoeffneterTermin.AllDay = appointment.AllDay;
			geoeffneterTermin.Start = appointment.Start;
			geoeffneterTermin.End = appointment.End;
			geoeffneterTermin.Description = appointment.Description;
			geoeffneterTermin.CustomFields["Employee"] = appointment.CustomFields["Employee"];
			geoeffneterTermin.CustomFields["BeWoResourceOid"] = appointment.CustomFields["BeWoResourceOid"];

			if (!appointment.Type.Equals(AppointmentType.ChangedOccurrence))
				return new ResourceAppointmentEditForm(this, control, appointment, mAllEmployees, mCategories2Resources, 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["Employee"] = specialCustomFields["Employee"];
				appointment.CustomFields["BeWoResourceOid"] = specialCustomFields["BeWoResourceOid"];
				geoeffneterTermin.CustomFields["Employee"] = specialCustomFields["Employee"];
				geoeffneterTermin.CustomFields["BeWoResourceOid"] = specialCustomFields["BeWoResourceOid"];
			}
			
			return new ResourceAppointmentEditForm(this, control, appointment, mAllEmployees, mCategories2Resources, SelectedFilterObject);
		}

		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.Start == zwei.Start &&
			       eins.End == zwei.End &&
				   eins.CustomFields["Employee"] == zwei.CustomFields["Employee"] &&
				   eins.CustomFields["BeWoResourceOid"] == zwei.CustomFields["BeWoResourceOid"];
			       
		}

		public void AddCustomFieldsMapping(SchedulerStorage schedulerStorage)
		{
			var bewoResourceOidMapping = new SchedulerCustomFieldMapping("BeWoResourceOid", "BeWoResourceOid");
			var employeeMapping = new SchedulerCustomFieldMapping("Employee", "Employee");

			schedulerStorage.AppointmentStorage.CustomFieldMappings.Add(bewoResourceOidMapping);
			schedulerStorage.AppointmentStorage.CustomFieldMappings.Add(employeeMapping);
		}

		public void InitNewAppointment(Appointment appointment)
		{
			appointment.CustomFields["BeWoResourceOid"] = null;
			ServiceFacade.DoEmployeeServiceSync(s => appointment.CustomFields["Employee"] = s.LoadCompactEmployee(BeWoApp.LoggedOnEmployee.EmployeeOid.Value));
			appointment.StatusId = 0;
		}

		public bool FilterAppointment(string filterCategory, object selectedObject, Appointment appointment, TimeInterval interval = null)
		{
			var selectedObjectType = selectedObject == null ? null : selectedObject.GetType();
			var result = false;

			SelectedFilterObject = selectedObject;

			if (selectedObjectType == typeof(CompactEmployeeDC))
			{
				result = !(appointment.CustomFields["Employee"] != null && 
					     appointment.CustomFields["Employee"].Equals(selectedObject as CompactEmployeeDC));
			}

			if (selectedObjectType == typeof(ResourceDC))
			{
				var obj = selectedObject as ResourceDC;
				result = !(obj != null && 
					     appointment.CustomFields["BeWoResourceOid"] != null &&
						 appointment.CustomFields["BeWoResourceOid"].Equals(obj.ResourceOid));
			}

			if (selectedObjectType == typeof(ValueListEntryDC))
			{
				var obj = selectedObject as ValueListEntryDC;
				result = !(appointment.CustomFields["BeWoResourceOid"] != null &&
						 obj != null &&
						 mCategories2Resources[obj].Any(a => a.ResourceOid.Equals(appointment.CustomFields["BeWoResourceOid"])));
			}

			// false => alle werden angezeigt
			return result;
		}

		public void InitChangedOccurrencyCustomFields<T>(IEnumerable<T> appList) where T : IDataContract
		{
			var liste = (List<ResourceAppointmentDC>) 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["Employee"] = termin.Employee;
				customFieldCollection["BeWoResourceOid"] = termin.Resource.ResourceOid;

				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;
			}
		}
	}
}
