using System;
using System.Collections.Generic;
using System.Windows.Forms;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BeWo.Service.Plugins;
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.Extensions;
using BS.Shared.Services;
using DevExpress.XtraCharts.Design;

namespace BeWo.SBBMainzReports.Calculations
{
	public class SBBMainzCalculations : BS.Shared.Services.Calculations
	{
		public override decimal? GetApprovedAmount(AccountingPeriod ap, SupportConceptApprovalPeriodDC scap)
		{
			return base.GetApprovedAmount(ap, scap);
		}

		public override decimal? GetApprovedAmountTotal(SupportConceptCostBearerRelDC relDC)
		{
			return base.GetApprovedAmountTotal(relDC);
		}

		public override decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC, DateTime date)
		{
			return base.GetApprovedHoursPerWeek(relDC, date);
		}

		public override decimal? GetApprovedHoursTotal(SupportConceptApprovalPeriodDC scap, List<CostRatePeriodDC> costRatePeriods)
		{
			return base.GetApprovedHoursTotal(scap, costRatePeriods);
		}

		public override decimal? GetApprovedHoursForPeriod(SupportConceptApprovalPeriodDC scap,
			List<CostRatePeriodDC> costRatePeriods,
			DateTime? start,
			DateTime? end)
		{
			if (!start.HasValue || !end.HasValue)
			{
				return null;
			}

			var periodInDays = (end.Value.Date - start.Value.Date).Days + 1;
			var periodSpan = new DateTimeSpan();
			periodSpan.StartDate = start.Value;
			periodSpan.EndDate = end.Value;

			var approvedHoursPerDayNormal = base.GetApprovedHoursPerDay(scap, costRatePeriods);

			var totalApproved = approvedHoursPerDayNormal * periodInDays;
			if (approvedHoursPerDayNormal.HasValue)
			{
				var period = DAOFactory.GenericDAO.LoadByID<SupportConceptApprovalPeriod>(scap.SupportConceptApprovalPeriodOid.Value);
				var absenceTimes = period.CostBearer2SupportConcept.SupportConcept.Customer.AbsenceTimes;
				var allAbsenceWeeks = GetAbsenceWeeks(absenceTimes, end);

				int daysOverlapping = 0;
				decimal maxAbrechenbarInOverlappingDays = 0;

				foreach (var weekSpan in allAbsenceWeeks.Keys)
				{
					decimal maxAbrechendbar = allAbsenceWeeks[weekSpan]/60;
					decimal maxAbrechenbarPerDay = maxAbrechendbar/7;
					
					if (weekSpan.Overlaps(periodSpan) && maxAbrechenbarPerDay < approvedHoursPerDayNormal)
					{
						if (weekSpan.StartDate < periodSpan.StartDate)
						{
							weekSpan.StartDate = periodSpan.StartDate;
						}
						if (weekSpan.EndDate > periodSpan.EndDate)
						{
							weekSpan.EndDate = periodSpan.EndDate;
						}
						var daysInWeek = (weekSpan.EndDate - weekSpan.StartDate).Days + 1;

						daysOverlapping += daysInWeek;
						maxAbrechenbarInOverlappingDays += daysInWeek*maxAbrechenbarPerDay;
					}

				}

				int daysNormal = periodInDays - daysOverlapping;
				totalApproved = approvedHoursPerDayNormal*daysNormal;
				totalApproved += maxAbrechenbarInOverlappingDays;

			}



			return totalApproved;
		}

		public override decimal? GetApprovedHoursPerDay(SupportConceptApprovalPeriodDC scapDc, List<CostRatePeriodDC> costRatePeriods)
		{
			if (!scapDc.EndDate.HasValue || !scapDc.StartDate.HasValue) // || scap.ApprovedFixedAmount.HasValue)
			{
				return null;
			}

			var serviceUnit = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, scapDc.EndDate.Value);

			
			decimal? approvedMinutesPerDay = null;
 
			if (scapDc.ApprovedBETotal.HasValue)
			{

				var scapDurationInDays = (scapDc.EndDate.Value - scapDc.StartDate.Value).Days + 1;
				decimal approvedBEPerDay = 0;
				if (scapDurationInDays != 0)
				{
					var total = scapDc.ApprovedBETotal.Value;
					approvedBEPerDay = total/scapDurationInDays;
				}

				approvedMinutesPerDay = serviceUnit.GetBEInMinutes(approvedBEPerDay);
			}

			else if (scapDc.ApprovedBEPerInterval.HasValue)
			{
				var approvedBEPerDay = this.ConvertPerIntervall2PerDay(scapDc.ApprovedBEPerInterval.Value, scapDc.ApprovedBEInterval.Value);
				approvedMinutesPerDay = serviceUnit.GetBEInMinutes(approvedBEPerDay);
			}

			if (approvedMinutesPerDay.HasValue)
			{
				

				return approvedMinutesPerDay.Value/60m;
			}
			return null;
		}

		//private Dictionary<DateTimeSpan, decimal> GetAbsencesTimesNotBillableWholeWeeks(SupportConceptCostBearerRelDC relDC)
		//{
		//	var cb2sc = DAOFactory.GenericDAO.LoadByID<CostBearer2SupportConcept>(relDC.CostBearer2SupportConceptOid.Value);
		//	var absenceTimes = cb2sc.SupportConcept.Customer.AbsenceTimes;

		//	return GetAbsencesTimesNotBillableWholeWeeks(absenceTimes, relDC.EndDate);
		//}

		//private decimal GetAbsenceHours(SupportConceptCostBearerRelDC relDC)
		//{
		//	var cb2sc = DAOFactory.GenericDAO.LoadByID<CostBearer2SupportConcept>(relDC.CostBearer2SupportConceptOid.Value);
		//	var absenceTimes = cb2sc.SupportConcept.Customer.AbsenceTimes;

		//	return GetAbsenceHours(absenceTimes, relDC.EndDate);
		//}

		//private decimal GetAbsenceHours(IList<AbsenceTime> absenceTimes, DateTime? maxEndTime)
		//{
		//	decimal absenceMinutes = 0;
			
		//	if (absenceTimes != null)
		//	{
		//		var spans = GetAbsencesTimesNotBillableWholeWeeks(absenceTimes, maxEndTime);

		//		foreach (var pair in spans)
		//		{
		//			absenceMinutes += pair.Value;
		//		}
		//	}

		//	return absenceMinutes / 60;
		//}

		private Dictionary<DateTimeSpan, decimal> GetAbsenceWeeks(IList<AbsenceTime> absenceTimes, DateTime? maxEndTime)
		{
			
			var lResult = new Dictionary<DateTimeSpan, decimal>();
			IList<AbsenceTime> times = absenceTimes;
			foreach (AbsenceTime at in times)
			{
				IEnumerable<DateTimeSpan> weeks = GetWholeWeeks(at.AbsenceSpan, maxEndTime);
				foreach (DateTimeSpan span in weeks)
				{
					if (at.AbsenceReason.BillableMinutes.HasValue && at.AbsenceReason.BillableMinutes.Value > 0 && !lResult.ContainsKey(span))
					{
						lResult.Add(span, at.AbsenceReason.BillableMinutes.Value);
					}
				}
			}


			return lResult;
		}

		public IEnumerable<DateTimeSpan> GetWholeWeeks(DateTimeSpan span, DateTime? maxEndTime)
		{
			var lResult = new List<DateTimeSpan>();

			DateTime startDate = span.StartDate;
			DateTime firstSunday = GetNextSunday(startDate);

			startDate = firstSunday.AddDays(1);

			DateTime totalEnddate = span.EndDate;
			if (totalEnddate == DateTime.MaxValue.Date)
			{
				if (maxEndTime.HasValue)
				{
					totalEnddate = maxEndTime.Value;
				}
				else
				{
					totalEnddate = span.StartDate.AddYears(1);
				}
			}

			while (startDate <= totalEnddate)
			{
				var endDate = startDate.AddDays(6);

				var lCurrentWorkWeek = new DateTimeSpan
				{
					StartDateTime = startDate,
					EndDateTime = endDate
				};

				lResult.Add(lCurrentWorkWeek);

				startDate = endDate.AddDays(1);
			}

			return lResult;
		}

		private DateTime GetNextSunday(DateTime startDate)
		{
			DateTime dt = startDate;
			while (dt.DayOfWeek != DayOfWeek.Sunday)
			{
				dt = dt.AddDays(1);
			}
			return dt;
		}
	}
}
