using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Xml;
using System.Xml.Linq;

using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.Core;
using BeWo.Service.DCEntityMapper;
using BeWo.Service.ServiceContracts;

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

using DevExpress.XtraScheduler;
using Resource = BeWo.Data.Entities.Resource;
using Utils = BeWo.Service.Core.Utils;

namespace BeWo.Service.ServiceImplementations
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
    public class ResourceServiceImp : IResourceService
    {
        public void DeactivateResource(long pOid, long pVersion)
        {
            try
            {
                ServiceLogic.SetActivationType<Resource>(new Dictionary<long, long> { { pOid, pVersion } }, ActivationTypeId.Deleted);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public void DeactivateResources(Dictionary<long, long> pOid2Version)
        {
            try
            {
                ServiceLogic.SetActivationType<Resource>(pOid2Version, ActivationTypeId.Deleted);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public void DeleteBooking(long pOid, long pVersion)
        {
            try
            {
                this.DeleteBookings(new Dictionary<long, long> { { pOid, pVersion } });
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public void DeleteBookings(Dictionary<long, long> pOid2Version)
        {
            try
            {
                var lOriginals = DAOFactory.GenericDAO.LoadByIDs<ResourceBookingSequence>(pOid2Version.Select(e => e.Key));
                lOriginals.DoForEach(or => MapperFactory.BookingSequenceDC_ResourceBookingSequence.ConcurrencyCheck(pOid2Version[or.Oid.Value], or));

                DAOFactory.GenericDAO.Delete(lOriginals);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public void DeleteResource(long pOid, long pVersion)
        {
            try
            {
                this.DeleteResources(new Dictionary<long, long> { { pOid, pVersion } });
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public void DeleteResources(Dictionary<long, long> pOid2Version)
        {
            try
            {
                var lOriginals = DAOFactory.GenericDAO.LoadByIDs<Resource>(pOid2Version.Select(e => e.Key));
                lOriginals.DoForEach(or => MapperFactory.ResourceDC_Resource.ConcurrencyCheck(pOid2Version[or.Oid.Value], or));

                DAOFactory.GenericDAO.Delete(lOriginals);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<BookingSequenceDC> GetAllBookings(DateTime pStartSpan, DateTime pEndSpan)
        {
            try
            {
                var t = DAOFactory.SearchDAO.FindBookings(pStartSpan, pEndSpan);
				//### Kalender
				//ConvertResourceBookingsToResourceAppointments();
                return MapperFactory.BookingSequenceDC_ResourceBookingSequence.MapToNewDCs(t);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<BookingSequenceDC> GetAllBookingsByResource(long pResourceOid, DateTime pStartSpan, DateTime pEndSpan)
        {
            try
            {
                var t = DAOFactory.SearchDAO.FindBookings(pResourceOid, pStartSpan, pEndSpan);
                return MapperFactory.BookingSequenceDC_ResourceBookingSequence.MapToNewDCs(t);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<ResourceDC> GetAllResources()
        {
            try
            {
                return MapperFactory.ResourceDC_Resource.MapToNewDCs(DAOFactory.GenericDAO.GetAllActive<Resource>());
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<long> InsertNewBookings(List<BookingSequenceDC> pBookings)
        {
            try
            {
                var lEntities = MapperFactory.BookingSequenceDC_ResourceBookingSequence.MapToNewEntities(pBookings);
                DAOFactory.GenericDAO.Insert(lEntities);
                return lEntities.Select(e => e.Oid.Value).ToList();
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<BookingSequenceDC> InsertNewBookingSequenceDCs(IEnumerable<BookingSequenceDC> pBookings)
        {
            try
            {
                var lEntities = MapperFactory.BookingSequenceDC_ResourceBookingSequence.MapToNewEntities(pBookings);
                DAOFactory.GenericDAO.Insert(lEntities);
                return MapperFactory.BookingSequenceDC_ResourceBookingSequence.MapToNewDCs(lEntities);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<long> InsertNewResources(List<ResourceDC> pResources)
        {
            try
            {
                List<Resource> lResources = MapperFactory.ResourceDC_Resource.MapToNewEntities(pResources);
                DAOFactory.GenericDAO.Insert(lResources);
                return lResources.Select(r => r.Oid.Value).ToList();
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<long> UpdateBookings(List<BookingSequenceDC> pBookings)
        {
            try
            {
                var lOriginals = DAOFactory.GenericDAO.LoadByIDs<ResourceBookingSequence>(pBookings.Select(b => b.SequenceOid.Value));
                MapperFactory.BookingSequenceDC_ResourceBookingSequence.MergeWithEntitys(pBookings, lOriginals);
                DAOFactory.GenericDAO.Update(lOriginals);
                return lOriginals.Select(b => b.Oid.Value).ToList();
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<BookingSequenceDC> UpdateBookingSequenceDCs(List<BookingSequenceDC> pBookings)
        {
            try
            {
                var lOriginals = DAOFactory.GenericDAO.LoadByIDs<ResourceBookingSequence>(pBookings.Select(b => b.SequenceOid.Value));
                MapperFactory.BookingSequenceDC_ResourceBookingSequence.MergeWithEntitys(pBookings, lOriginals);
                DAOFactory.GenericDAO.Update(lOriginals);
                return MapperFactory.BookingSequenceDC_ResourceBookingSequence.MapToNewDCs(lOriginals);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<ResourceDC> UpdateResourceDCs(List<ResourceDC> pResource)
        {
            try
            {
                var lOriginals = DAOFactory.GenericDAO.LoadByIDs<Resource>(pResource.Select(b => b.ResourceOid.Value));
                MapperFactory.ResourceDC_Resource.MergeWithEntitys(pResource, lOriginals);
                DAOFactory.GenericDAO.Update(lOriginals);
                return MapperFactory.ResourceDC_Resource.MapToNewDCs(lOriginals);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

        public List<long> UpdateResources(List<ResourceDC> pResource)
        {
            try
            {
                var lOriginals = DAOFactory.GenericDAO.LoadByIDs<Resource>(pResource.Select(b => b.ResourceOid.Value));
                MapperFactory.ResourceDC_Resource.MergeWithEntitys(pResource, lOriginals);
                DAOFactory.GenericDAO.Update(lOriginals);
                return lOriginals.Select(b => b.Oid.Value).ToList();
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

	    public List<ResourceAppointmentDC> UpdateResourceAppointments(List<ResourceAppointmentDC> pResourceAppointments)
	    {
		    try
		    {
			    var lOriginals = DAOFactory.GenericDAO.LoadByIDs<ResourceAppointment>(pResourceAppointments.Select(b => b.ResourceAppointmentOid.Value));
				MapperFactory.ResourceAppointmentDC_ResourceAppointment.MergeWithEntitys(pResourceAppointments, lOriginals);
				DAOFactory.GenericDAO.Update(lOriginals);
			    return MapperFactory.ResourceAppointmentDC_ResourceAppointment.MapToNewDCs(lOriginals);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public List<ResourceAppointmentDC> InsertNewResourceAppointments(IEnumerable<ResourceAppointmentDC> pResourceAppointments)
		{
			try
			{
				var lResources = MapperFactory.ResourceAppointmentDC_ResourceAppointment.MapToNewEntities(pResourceAppointments);

				DAOFactory.GenericDAO.Insert(lResources);
				return MapperFactory.ResourceAppointmentDC_ResourceAppointment.MapToNewDCs(lResources);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
		}

		public void DeleteResourceAppointments(Dictionary<long, long> pOid2Version)
	    {
		    try
		    {
				var lOriginals = DAOFactory.GenericDAO.LoadByIDs<ResourceAppointment>(pOid2Version.Select(e => e.Key));
				lOriginals.DoForEach(or => MapperFactory.ResourceAppointmentDC_ResourceAppointment.ConcurrencyCheck(pOid2Version[or.Oid.Value], or));

				DAOFactory.GenericDAO.Delete(lOriginals);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    private void ConvertResourceBookingsToResourceAppointments()
	    {
		    try
		    {
				const string selectQuery = "SELECT ResourceBookingSequenceOid, Oid FROM resourcebooking WHERE IsActive = 1 AND IsDeleted = 0";
				var dataSet = DAOFactory.AdoDAO.ExecuteQuery(selectQuery);
				var dataReader = dataSet.CreateDataReader();
			    var rbs2rb = new Dictionary<long, List<long>>();
				var newSchedulerAppointments = new List<SchedulerAppointmentDC>();
				var sequence2booking = new Dictionary<ResourceBookingSequence, List<ResourceBooking>>();
				var allResourceBookings = DAOFactory.GenericDAO.GetAllActive<ResourceBooking>();

				while (dataReader.Read())
			    {
					rbs2rb.AddOrUpdateValueInDictionary((long) dataReader.GetValue(0), new List<long> {(long) dataReader.GetValue(1)});
			    }
			    
			    if (allResourceBookings.Count == 0) return;

				allResourceBookings = allResourceBookings.OrderBy(o => o.Oid).ToList();

			    foreach (var b in allResourceBookings)
			    {
				    if (!sequence2booking.ContainsKey(b.Sequence))
					    sequence2booking.Add(b.Sequence, new List<ResourceBooking> {b});
				    else
					    sequence2booking[b.Sequence].Add(b);
			    }

			    foreach (var kvp in sequence2booking)
			    {
				    var sequence = kvp.Key;
				    var frequencyType = sequence.FrequencyType;

				    foreach (var booking in kvp.Value)
				    {
					    var originator = DAOFactory.SearchDAO.FindEmployeeByFullname(booking.InsUser) ?? booking.Employee;

					    if (booking.Sequence.FrequencyType.Equals(ResourceBookingFrequencyType.MonthlyOnLastDay))
					    {
						    var singleBookings = ConvertType6ResourceAppointment(booking);
							newSchedulerAppointments.AddRange(singleBookings);
						    continue;
					    }

					    var employeeList = new List<Employee2SchedulerAppointmentDC>();
						if(booking.Employee != null)
							employeeList.Add(new Employee2SchedulerAppointmentDC
							{
								Employee = MapperFactory.CompactEmployeeDC_Employee.MapToNewDC(booking.Employee),
								ParticipationAnswer = ParticipationAnswer.Zusage,
								IsPC_CheckedTs = null,
								IsPChanged = false
							});

					    var resourceList = new List<ResourceDC> {MapperFactory.ResourceDC_Resource.MapToNewDC(booking.Sequence.Resource)};

					    var newSchedulerAppointment = new SchedulerAppointmentDC
					    {
						    AllDay = booking.Start.Equals(booking.End),
						    CustomerList = new List<CompactCustomerDC>(),
						    Description = booking.Notice,
						    EmployeeList = employeeList,
						    EndDate = booking.End,
						    StartDate = booking.Start,
						    IsPrivate = false,
							ResourceList = resourceList,
						    Subject = booking.Notice,
						    Originator = MapperFactory.CompactEmployeeDC_Employee.MapToNewDC(originator),
						    Type = 0,
						    FormerBookingSequenceOid = booking.Sequence.Oid
					    };

					    if (kvp.Value.Count > 1 && booking.SequencePosition > 0)
					    {
						    newSchedulerAppointment.Type = booking.IsDeleted ? 4 : 3;
					    }
					    else if(frequencyType > 0)
					    {
						    newSchedulerAppointment.Type = 1;
					    }

						if(!booking.Sequence.FrequencyType.Equals(ResourceBookingFrequencyType.Once))
							newSchedulerAppointment.RecurrenceInfo = CreateRecurrenceInfo(booking).ToXml();

						if (newSchedulerAppointments.FirstOrDefault(f => f.FormerBookingSequenceOid.Equals(sequence.Oid)) != null)
						{
							var x = newSchedulerAppointments.First(f => f.FormerBookingSequenceOid.Equals(sequence.Oid) && f.RecurrenceInfo != null).RecurrenceInfo;

							using (var reader = XmlReader.Create(new StringReader(x)))
							{
								reader.ReadToFollowing("RecurrenceInfo");
								reader.MoveToAttribute("Id");
								var id = reader.Value;

								var doc = XDocument.Load(new StringReader(newSchedulerAppointment.RecurrenceInfo));
								var xElement = doc.Element("RecurrenceInfo");
								if (xElement != null)
								{
									xElement.RemoveAttributes();
									xElement.Add(new XAttribute("Id", id));
									xElement.Add(new XAttribute("Index", booking.SequencePosition));
								}

								newSchedulerAppointment.RecurrenceInfo = doc.ToString();
							}
						}

						newSchedulerAppointments.Add(newSchedulerAppointment);
				    }
			    }

				foreach (var resourcebooking in allResourceBookings)
				{
					resourcebooking.IsActive = ActivationTypeId.Deleted;
					resourcebooking.Sequence.IsActive = ActivationTypeId.Deleted;
				}

				DAOFactory.GenericDAO.Update(allResourceBookings);
				InsertSchedulerAppointments(newSchedulerAppointments);
		    }
			catch (Exception ex)
			{
				throw Utils.CreateBeWoFaultException(ex);
			}
	    }

	    private static RecurrenceInfo CreateRecurrenceInfo(ResourceBooking booking)
	    {
		    var sequence = booking.Sequence;
		    var recInfo = new RecurrenceInfo {Range = RecurrenceRange.EndByDate};
		    DateTime? datum = null;

			// Rekonstruktion der gelöschten Zeiten (der neue Termin wird sonst nicht als gelöscht angezeigt)
			if (booking.Start == null)
			{
				var firstBooking = DAOFactory.SearchDAO.GetFirstResourceBookingFromSequence(sequence.Oid.Value);
				var sequenceStartDate = firstBooking.Start.Value;

				// Täglich
				if (sequence.FrequencyType.Equals(ResourceBookingFrequencyType.Daily))
				{
					datum = sequenceStartDate.AddDays(sequence.RepeatInterval * booking.SequencePosition);
				}

				// Wöchentlich
				if (sequence.FrequencyType.Equals(ResourceBookingFrequencyType.Weekly))
				{
					datum = sequenceStartDate.AddDays(sequenceStartDate.Day + 7 * sequence.RepeatInterval * booking.SequencePosition);
				}

				// Monatlich
				if (sequence.FrequencyType.Equals(ResourceBookingFrequencyType.Monthly))
				{
					datum = sequenceStartDate.AddMonths(sequence.RepeatInterval*booking.SequencePosition);
				}

				// X. Wochentag des Monats
				if (sequence.FrequencyType.Equals(ResourceBookingFrequencyType.MonthlyOnFirstDay))
				{
					datum = sequenceStartDate.AddMonthsDayDependent(sequence.RepeatInterval*booking.SequencePosition, true);
				}

				booking.Start = datum;
				booking.End = booking.Start.Value.AddMinutes((firstBooking.End.Value - firstBooking.Start.Value).TotalMinutes);
			}

		    if (booking.Start != null)
			    recInfo.Start = booking.Start.Value;
		    if (sequence.SequenceEnd != null)
			    recInfo.End = sequence.SequenceEnd.Value;

			if (sequence.RepeatInterval > 1)
				recInfo.Periodicity = sequence.RepeatInterval;

			switch (booking.Sequence.FrequencyType)
			{
				case ResourceBookingFrequencyType.Daily:
					recInfo.OccurrenceCount = ((recInfo.End - recInfo.Start).Days + 1) /
											  booking.Sequence.RepeatInterval;
					recInfo.Type = RecurrenceType.Daily;
					break;
				case ResourceBookingFrequencyType.Weekly:
					recInfo.OccurrenceCount = ((recInfo.End - recInfo.Start).Days / 7 + 1) /
											  booking.Sequence.RepeatInterval;
					recInfo.Type = RecurrenceType.Weekly;
					recInfo.WeekDays = GetWeekDay(recInfo.Start.DayOfWeek.ToString());
					break;
				case ResourceBookingFrequencyType.Monthly:
					recInfo.Type = RecurrenceType.Monthly;
					recInfo.OccurrenceCount = ((recInfo.End - recInfo.Start).Days / 30 + 1) /
											  booking.Sequence.RepeatInterval;
					recInfo.WeekDays = GetWeekDay(recInfo.Start.DayOfWeek.ToString());
					recInfo.DayNumber = recInfo.Start.Day;
					recInfo.WeekOfMonth = WeekOfMonth.None;
					break;
				case ResourceBookingFrequencyType.MonthlyOnFirstDay:
					recInfo.Type = RecurrenceType.Monthly;
					recInfo.OccurrenceCount = ((recInfo.End - recInfo.Start).Days / 30 + 1) /
											  booking.Sequence.RepeatInterval;
					recInfo.WeekDays = GetWeekDay(recInfo.Start.DayOfWeek.ToString());
					recInfo.WeekOfMonth = recInfo.Start.GetWeekOfMonth(DayOfWeek.Sunday);
					break;
			}
		    
		    return recInfo;
	    }

		private static IEnumerable<SchedulerAppointmentDC> ConvertType6ResourceAppointment(ResourceBooking booking)
		{
			var sequence = booking.Sequence;
			if (sequence.Oid == null) return null;

			var firstBooking = DAOFactory.SearchDAO.GetFirstResourceBookingFromSequence(sequence.Oid.Value);

			if (firstBooking.Start == null || firstBooking.End == null || sequence.SequenceEnd == null) return null;

			// X. Letzten Wochentag im Monat berechnen für den gesamten Zeitraum (Typ 3 und 4 beachten, oder nicht?)
			var duration = (firstBooking.End.Value - firstBooking.Start.Value).TotalMinutes;
			var newDates = new List<DateTime>{firstBooking.Start.Value};

			newDates.AddRange(CalculateLastXInMonthDates(DAOFactory.SearchDAO.GetExcludedBookingSequencePositions(sequence.Oid.Value), firstBooking.Start.Value, sequence));

			var employee = MapperFactory.CompactEmployeeDC_Employee.MapToNewDC(booking.Employee);
			var employeeList = new List<Employee2SchedulerAppointmentDC>();
			if (booking.Employee != null)
				employeeList.Add(new Employee2SchedulerAppointmentDC
				{
					Employee = employee,
					ParticipationAnswer = ParticipationAnswer.Zusage,
					IsPC_CheckedTs = null,
					IsPChanged = false
				});

			var resourceList = new List<ResourceDC> { MapperFactory.ResourceDC_Resource.MapToNewDC(booking.Sequence.Resource) };

			return newDates.Select(date => new SchedulerAppointmentDC
			{
				AllDay = false, 
				CustomerList = new List<CompactCustomerDC>(), 
				Description = firstBooking.Notice, 
				EmployeeList = employeeList, 
				EndDate = date.AddMinutes(duration), 
				FormerBookingSequenceOid = firstBooking.Sequence.Oid, 
				IsPrivate = false, 
				Originator = employee, 
				ResourceList = resourceList, 
				StartDate = date, 
				Subject = firstBooking.Notice, 
				Type = 0
			}).ToList();
	    }

		private static IEnumerable<DateTime> CalculateLastXInMonthDates(ICollection<int> excludedBookingSequencePositions, DateTime pSequenceStart, ResourceBookingSequence pSequence)
	    {
			// bearbeitete und gelöschte Termine nicht beachten
			var result = new List<DateTime>();

			var lCurrentPosition = 0;
			var lCurrentSequencePosition = 1;
			var lCurrentStart = pSequenceStart.AddMonthsDayDependent(1, false);

			while (lCurrentStart <= pSequence.SequenceEnd)
			{
				lCurrentPosition++;
				
				if (lCurrentPosition % pSequence.RepeatInterval == 0 && !excludedBookingSequencePositions.Contains(lCurrentSequencePosition))
				{
					result.Add(lCurrentStart);
					lCurrentSequencePosition++;
				}

				lCurrentStart = lCurrentStart.AddMonthsDayDependent(1, false);
			}

			return result;
	    }

		private static WeekDays GetWeekDay(string name)
		{
			switch (name)
			{
				case "Sunday":
					return WeekDays.Sunday;
				case "Monday":
					return WeekDays.Monday;
				case "Tuesday":
					return WeekDays.Thursday;
				case "Wednesday":
					return WeekDays.Wednesday;
				case "Thursday":
					return WeekDays.Thursday;
				case "Friday":
					return WeekDays.Friday;
				case "Saturday":
					return WeekDays.Saturday;
				default:
					return WeekDays.Sunday;
			}
		}

	    public List<ResourceAppointmentDC> GetAllResourceAppointments()
	    {
			try
			{
				var lResources = DAOFactory.GenericDAO.GetAll<ResourceAppointment>();
				return MapperFactory.ResourceAppointmentDC_ResourceAppointment.MapToNewDCs(lResources);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    public List<SchedulerAppointmentDC> GetAllSchedulerAppointments()
	    {
		    try
		    {
			    var lSchedulerAppointments = DAOFactory.GenericDAO.GetAll<SchedulerAppointment>();
			    return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(lSchedulerAppointments);
		    }
		    catch (Exception e)
		    {
			    throw Utils.CreateBeWoFaultException(e);
		    }
	    }

	    public List<SchedulerAppointmentDC> GetAllActiveSchedulerAppointments()
	    {
			try
			{
				var lSchedulerAppointments = DAOFactory.GenericDAO.GetAllActive<SchedulerAppointment>();
				return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(lSchedulerAppointments);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    public void DeleteSchedulerAppointments(Dictionary<long, long> pOid2Version)
	    {
			try
			{
				var lOriginals = DAOFactory.GenericDAO.LoadByIDs<SchedulerAppointment>(pOid2Version.Select(n => n.Key));
				lOriginals.DoForEach(on => MapperFactory.SchedulerAppointmentDCSchedulerAppointment.ConcurrencyCheck(pOid2Version[on.Oid.Value], on));

				DAOFactory.GenericDAO.Delete(lOriginals);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    public List<SchedulerAppointmentDC> InsertSchedulerAppointments(IList<SchedulerAppointmentDC> pSchedulerAppointments)
	    {
		    try
		    {
				foreach(var item in pSchedulerAppointments)
				{
					try
					{
						item.EmployeeList.ForEach(each =>
						{
							each.Employee2SchedulerAppointmentOid = null;
							each.Employee2SchedulerAppointmentVersion = null;
							each.SchedulerAppointmentOid = null;
							each.IsPChanged = false;
							each.IsPC_CheckedTs = null;
						});

						var lRelations = MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewEntities(item.EmployeeList);
						DAOFactory.GenericDAO.Insert(lRelations);
						var neu = MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewDCs(lRelations);
						item.EmployeeList = neu;
					}
					catch (Exception exception)
					{
						throw Utils.CreateBeWoFaultException(exception);
					}
				}

			    var lAppointments = MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewEntities(pSchedulerAppointments);

				DAOFactory.GenericDAO.Insert(lAppointments);
			    return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(lAppointments);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    public List<SchedulerAppointmentDC> UpdateSchedulerAppointments(List<SchedulerAppointmentDC> pSchedulerAppointments)
	    {
		    try
		    {
			    if (pSchedulerAppointments.Any(p => p.SchedulerAppointmentOid == null))
			    {
			        return new List<SchedulerAppointmentDC>();
			    }

			    var lOriginals = DAOFactory.GenericDAO.LoadByIDs<SchedulerAppointment>(pSchedulerAppointments.Select(b => b.SchedulerAppointmentOid.Value));
			    var allegleich = true;
				// Die geänderten Anwesenheiten updaten und die entfernten löschen
				foreach (var item in pSchedulerAppointments)
				{
					allegleich = false;
					var lNewRelations = MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewEntities(item.EmployeeList.Where(e2a => e2a.Employee2SchedulerAppointmentOid == null));
					var lOldRelations = MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewDCs(DAOFactory.GenericDAO.LoadByIDs<Employee2SchedulerAppointment>(item.EmployeeList.Where(w => w.Employee2SchedulerAppointmentOid != null).ToList().Select(s => s.Employee2SchedulerAppointmentOid.Value)));

					var geaenderte = new List<Employee2SchedulerAppointmentDC>();
					foreach (var i in lOldRelations)
					{
						geaenderte.AddRange(item.EmployeeList.Where(o => o.Employee2SchedulerAppointmentOid == i.Employee2SchedulerAppointmentOid &&
							(o.Employee2SchedulerAppointmentVersion != i.Employee2SchedulerAppointmentVersion || o.ParticipationAnswer != i.ParticipationAnswer || o.IsPChanged != i.IsPChanged) &&
							!geaenderte.Contains(o)));
					}

					geaenderte = UpdateEmployee2SchedulerAppointments(geaenderte);
					
					var geloeschte = DAOFactory.SearchDAO.GetRemovedEmp2AppObjectsBySchAppOid(item.SchedulerAppointmentOid.Value).Where(g => !item.EmployeeList.Select(s => s.Employee2SchedulerAppointmentOid).Contains(g.Oid));
					DAOFactory.GenericDAO.Delete(geloeschte);

					DAOFactory.GenericDAO.Insert(lNewRelations);
					var neu = MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewDCs(lNewRelations);
					var alt = item.EmployeeList.Where(e2a => e2a.Employee2SchedulerAppointmentOid != null && !geaenderte.Contains(e2a)).ToList();
					alt.AddRange(neu);
					alt.AddRange(geaenderte);
					item.EmployeeList = alt;
				}

			    if (allegleich)
			    {
			        return pSchedulerAppointments;
			    }

			    MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MergeWithEntitys(pSchedulerAppointments, lOriginals);
				DAOFactory.GenericDAO.Update(lOriginals);
			    return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(lOriginals);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    public List<ResourceAppointmentDC> GetResourceAppointmentsById(IEnumerable<long> pOids)
	    {
		    try
		    {
			    var lAppointments = DAOFactory.GenericDAO.LoadByIDs<ResourceAppointment>(pOids);
			    return MapperFactory.ResourceAppointmentDC_ResourceAppointment.MapToNewDCs(lAppointments);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

	    public List<ResourceAppointmentDC> LoadPossibleDuplicates(string pRecurrenceInfo)
	    {
			try
			{
				var possibleDuplicates = DAOFactory.SearchDAO.GetResourceAppointmentExceptionsWithIndexAndId(pRecurrenceInfo);
				return MapperFactory.ResourceAppointmentDC_ResourceAppointment.MapToNewDCs(possibleDuplicates);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public List<SchedulerAppointmentDC> GetSchedulerAppointmentsById(IEnumerable<long> pOids)
	    {
		    try
		    {
			    var lSchedulerAppointments = DAOFactory.GenericDAO.LoadByIDs<SchedulerAppointment>(pOids);
			    return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(lSchedulerAppointments);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

        public SchedulerAppointmentDC GetSchedulerAppointmentByid(long oid)
        {
            try
            {
                var lSchedulerAppointments = DAOFactory.GenericDAO.GetByID<SchedulerAppointment>(oid);
                return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDC(lSchedulerAppointments);
            }
            catch (Exception e)
            {
                throw Utils.CreateBeWoFaultException(e);
            }
        }

	    public void DeactivateSchedulerAppointments(Dictionary<long, long> pOid2Version)
	    {
		    try
		    {
			    foreach (var kvp in pOid2Version)
					ServiceLogic.SetActivationType<SchedulerAppointment>(new Dictionary<long, long> { { kvp.Key, kvp.Value } }, ActivationTypeId.Deleted);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public List<SchedulerAppointmentDC> DeactivateSchedulerAppointmentsForSync(Dictionary<long, long> pOid2Version)
		{
			try
			{
				foreach (var kvp in pOid2Version)
					ServiceLogic.SetActivationType<SchedulerAppointment>(new Dictionary<long, long> { { kvp.Key, kvp.Value } }, ActivationTypeId.Deleted);

				return GetSchedulerAppointmentsById(pOid2Version.Keys);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
		}

	    public Dictionary<ValueListEntryDC, List<ResourceDC>> GetAllCategories2ResourcesInDictionary()
	    {
		    try
		    {
			    var ressourcen = MapperFactory.ResourceDC_Resource.MapToNewDCs(DAOFactory.GenericDAO.GetAllActive<Resource>());

				return ressourcen.Select(res => res.ResourceCategory)
								.Distinct()
								.OrderBy(cat => cat.TypeDescription)
								.Select(
									cat =>
										new
										{
											Key = cat,
											Value = ressourcen.Where(res => res.ResourceCategory.Equals(cat)).OrderBy(res => res.Name).ToList()
										}).ToDictionary(a => a.Key, a => a.Value);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public List<SchedulerAppointmentDC> GetAllOpenAppointmentsForEmployee(long pEmployeeOid)
		{
			try
			{
				var offeneTermine = DAOFactory.SearchDAO.GetAllOpenAppointmentsForEmployee(pEmployeeOid);
				offeneTermine.AddRange(DAOFactory.SearchDAO.GetAllParticipationNotificationsForEmployee(pEmployeeOid));

				return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(offeneTermine);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
		}

	    public List<Employee2SchedulerAppointmentDC> InsertEmployee2SchedulerAppointments(IEnumerable<Employee2SchedulerAppointmentDC> pEmployee2SchedulerAppointments)
	    {
			try
			{
				var lRelations = MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewEntities(pEmployee2SchedulerAppointments);

				DAOFactory.GenericDAO.Insert(lRelations);
				return MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewDCs(lRelations);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public List<Employee2SchedulerAppointmentDC> UpdateEmployee2SchedulerAppointments(List<Employee2SchedulerAppointmentDC> pEmployee2SchedulerAppointments)
		{
			try
			{
				
				var lOriginals = DAOFactory.GenericDAO.LoadByIDs<Employee2SchedulerAppointment>(pEmployee2SchedulerAppointments.Select(b => b.Employee2SchedulerAppointmentOid.Value));

				foreach (var item in pEmployee2SchedulerAppointments)
					item.IsPChanged = item.ParticipationAnswer != lOriginals.Find(f => f.Oid == item.Employee2SchedulerAppointmentOid).ParticipationAnswer;

				MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MergeWithEntitys(pEmployee2SchedulerAppointments, lOriginals);
				DAOFactory.GenericDAO.Update(lOriginals);
				return MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.MapToNewDCs(lOriginals);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
		}

		public void DeleteEmployee2SchedulerAppointments(Dictionary<long, long> pOid2Version)
		{
			try
			{
				var lOriginals = DAOFactory.GenericDAO.LoadByIDs<Employee2SchedulerAppointment>(pOid2Version.Select(e => e.Key));
				lOriginals.DoForEach(or => MapperFactory.Employee2SchedulerAppointmentDC_Employee2SchedulerAppointment.ConcurrencyCheck(pOid2Version[or.Oid.Value], or));

				DAOFactory.GenericDAO.Delete(lOriginals);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
		}

	    public List<SchedulerAppointmentDC> GetAllActiveAndNotDeclinedForEmployeeAppointments(long pEmployeeOid)
	    {
			try
			{
				var blubb = DAOFactory.SearchDAO.GetAllActiveAndNotDeclinedForEmployeeAppointments(pEmployeeOid);
				return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(blubb);
			}
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public bool OverlappingAppointmentsExist(DateTime start, DateTime end, List<long> employees, List<long> customers, List<long> resources, long originator, long? appointmentOid, string recurrenceId = "", int ocurrenceIndex = 0)
	    {
		    try
		    {
				return DAOFactory.SearchDAO.OverlappingAppointmentsExist(start, end, employees, customers, resources, originator, appointmentOid, recurrenceId, ocurrenceIndex);
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }

		public List<SchedulerAppointmentDC> GetAllActiveAppointmentsForEmployeeInInterval(DateTime start, DateTime end, long pEmployeeOid)
		{
			return GetAllActiveAppointmentsForEmployeeInInterval2(start, end, pEmployeeOid, false);
		}

		public List<SchedulerAppointmentDC> GetAllActiveAppointmentsForEmployeeInInterval2(DateTime start, DateTime end, long pEmployeeOid, bool pHasRightToSeeAllEmployeeAppointments)
	    {
		    try
			{
				var apps = pHasRightToSeeAllEmployeeAppointments ? 
					DAOFactory.SearchDAO.GetAllActiveAppointmentsInInterval(start, end) : 
					DAOFactory.SearchDAO.GetAllActiveAppointmentsForEmployeeInInterval(start, end, pEmployeeOid);

                return MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(apps).OrderBy(a => a.StartDate).ToList();
		    }
			catch (Exception e)
			{
				throw Utils.CreateBeWoFaultException(e);
			}
	    }
    }
}