using System;
using System.Collections.Generic;
using System.Linq;
using BeWo.Data.Entities;
using BeWo.Report.ReportObjects;

using BS.Shared.Extensions;


namespace BeWo.DiakonieKKKleve
{
	public class DiakonieKKKleveMitarbeiterstundenkontoBerechnung : MitarbeiterstundenkontoBerechnung
	{
		public override bool ShouldCreateDayDetails()
		{
			return true;
		}

		public override IList<MitarbeiterstundenkontoRO.DayDetail> CreateDayDetails(DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList<ServiceRecord> recordsAll)
		{
			IList<MitarbeiterstundenkontoRO.DayDetail> dayDetailList = new List<MitarbeiterstundenkontoRO.DayDetail>();

			decimal flmTotal = 0;
			decimal azTotal = 0;
			foreach (var sr in recordsAll)
			{
				if (sr.Start.HasValue && sr.Start >= berichtStart && sr.Start < berichtEnde && sr.End.HasValue)
				{
					decimal duration = 0;
					if (sr.GroupRoundedDuration.HasValue)
					{
						duration = sr.GroupRoundedDuration.Value;
					}
					else
					{
						duration = sr.RoundedDuration;
					}
					if (AddServiceRecordToDuration(sr))
					{
						MitarbeiterstundenkontoRO.DayDetail dd = CreateDayDetail(new DateTime(berichtStart.Year, berichtStart.Month, sr.Start.Value.Day), ed);
						dayDetailList.Add(dd);

						dd.MinutesTotal += duration;
						if (dd.ServiceRecords == null)
							dd.ServiceRecords = new List<ServiceRecord>();
						dd.ServiceRecords.Add(sr);

						CalculateSpecialHours(dd, sr, duration);


						DateTime dtEnd = sr.Start.Value.AddMinutes((double)duration);
						
						dd.StartDateTime1 = sr.Start;
							
						dd.EndDateTime1 = dtEnd;

						azTotal += duration;

					}
					else
					{
						
						if (sr.ServiceDescription.ServiceCategory.IsBillable)
						{
							flmTotal += duration;
						}
					}
				}
			}
			if (azTotal != 0)
			{
				ed.FlsDiff = (flmTotal/azTotal)*100; //Differenzen durch AbsenceTimes?
			}

			for (int i = berichtStart.Day; i <= DateTime.DaysInMonth(berichtStart.Year, berichtStart.Month); i++)
			{
				var start = new DateTime(berichtStart.Year, berichtStart.Month, i);
				
				var ah = GetAbsenceTimeDays(ed.Employee.AbsenceTimes, start, start.AddDays(1));

				if (ah.TageKrankheit.HasValue && ah.TageKrankheit > 0)
				{
					MitarbeiterstundenkontoRO.DayDetail dd = CreateDayDetail(start, ed);
					dayDetailList.Add(dd);

					dd.MinutesTotal = (ed.SollProTag * 60) / ah.TageKrankheit.Value;
				}
				if (ah.TageUrlaub.HasValue && ah.TageUrlaub > 0)
				{
					MitarbeiterstundenkontoRO.DayDetail dd = CreateDayDetail(start, ed);
					dayDetailList.Add(dd);

					dd.MinutesTotal = (ed.SollProTag * 60) / ah.TageUrlaub.Value;
				}
			}
			


			return dayDetailList.OrderBy(dd => dd.Date).ToList();
		}

		public override MitarbeiterstundenkontoRO.DayDetail CreateDayDetail(DateTime date, MitarbeiterstundenkontoRO.EmployeeDetail ed)
		{
			return new MitarbeiterstundenkontoRO.DayDetail { Date = date.Date, NightStart = 21, NightEnd = 6 };
		}

		public override void CalculateSpecialHours(MitarbeiterstundenkontoRO.DayDetail dd, ServiceRecord sr, decimal duration)
		{
			if (!sr.Start.HasValue || dd.NightStart == null || dd.NightEnd == null)
			{
				base.CalculateSpecialHours(dd, sr, duration);
				return;
			}

			var d1 = sr.Start.Value;

			var baseDate = new DateTime(d1.Year, d1.Month, d1.Day);

			var saturday = d1.GetNextDayOfWeek(DayOfWeek.Saturday);
			var sunday = d1.GetNextDayOfWeek(DayOfWeek.Sunday);
			var christmasEve = new DateTime(d1.Year, 12, 24);
			var newYearsEve = new DateTime(d1.Year, 12, 31);
			var unroundedEnd = d1.AddMinutes((double) duration);

			var saturdayStart = new DateTime(saturday.Year, saturday.Month, saturday.Day, 13, 0, 0);
			var saturdayEnd = new DateTime(saturday.Year, saturday.Month, saturday.Day, 21, 0, 0);

			var nightWorkingStart = baseDate.AddHours(dd.NightEnd.Value);
			var nightWorkingEnd = baseDate.AddHours(dd.NightStart.Value);

			dd.HoursSunday += (decimal) d1.GetIntersectingMinutes(unroundedEnd, sunday, sunday.AddHours(23).AddMinutes(59).AddSeconds(59))/60;

			dd.HoursHoliday2 += (decimal) d1.GetIntersectingMinutes(unroundedEnd, christmasEve, christmasEve.AddHours(23).AddMinutes(59).AddSeconds(59).AddMilliseconds(999))/60;
			dd.HoursHoliday2 += (decimal) d1.GetIntersectingMinutes(unroundedEnd, newYearsEve, newYearsEve.AddHours(23).AddMinutes(59).AddSeconds(59).AddMilliseconds(999))/60;

			dd.SpecialHours += calculateNightWorkingTimeInMinutes(nightWorkingStart, nightWorkingEnd, d1, unroundedEnd);

			if (IstFeiertag(d1))
			{
				dd.HoursHoliday += duration / 60;
			}

			dd.HoursSaturday += (decimal)d1.GetIntersectingMinutes(unroundedEnd, saturdayStart, saturdayEnd) / 60;
		}

		static decimal calculateNightWorkingTimeInMinutes(DateTime ns, DateTime ne, DateTime ds, DateTime de)
		{
			var result = 0d;

			var morgens = (ns - ds).TotalMinutes;
			var abends = (de - ne).TotalMinutes;
			if (ds > ne)
			{
				result -= (ds - ne).TotalMinutes;
			}

			if (de < ns)
			{
				result -= (ns - de).TotalMinutes;
			}

			result += (morgens > 0 ? morgens : 0) + (abends > 0 ? abends : 0);

			return Convert.ToDecimal(result) / 60m;
		}

		public override bool AddServiceRecordToDuration(ServiceRecord item)
		{
			if (item.ServiceDescription != null && item.ServiceDescription.ServiceCategory != null)
			{
				if (item.ServiceDescription.ServiceCategory.Name == "Arbeitszeit")
				{
					return true;
				}
			}
			return false;
		}

		public override List<DateTime> GetFeiertage(int year)
		{
			var list = base.GetFeiertage(year);

			list.Add(new DateTime(year, 12, 31));

			var osterSonntag = GetOsterSonntag(year);
			list.Add(osterSonntag.AddDays(-48)); // "Rosenmontag"

			return list;
		}

	}
}
