using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BeWo.Service.Plugins;
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;
using BS.Shared.Extensions;
using BS.Shared.Services;
using BeWo.Data;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.Core;
using BeWo.Service.DCEntityMapper;

namespace BeWo.Service.Invoicing
{
	public class SettlementInvoiceCreation : AbstractIDSpecificDefaultClass<SettlementInvoiceCreation>
	{
		public virtual Settlement2DC GenerateSettlementInvoice(CostBearer2SupportConcept c2s, DateTime? periodStart, DateTime? periodEnd)
		{
			Settlement2DC settlementInvoice = new Settlement2DC();

			settlementInvoice.CostBearer2SupportConceptOid = c2s.Oid;
			settlementInvoice.CustomerReferenceNumber = c2s.CustomerReferenceNumber;
			settlementInvoice.InvoiceDate = DateTime.Now.Date;
			settlementInvoice.ServiceUnit = MapperFactory.CostRatePeriodDC_CostRatePeriod.MapToNewDCs(c2s.CostBearer.CostRatePeriods)
				.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now);
			if (settlementInvoice.ServiceUnit == null)
			{
				settlementInvoice.ServiceUnit = new CostRatePeriodDC();
				settlementInvoice.ServiceUnit.CostRateType = CostRatePeriodType.MinutesPerServiceUnit;
				settlementInvoice.ServiceUnit.UnitName = "FLS";
				settlementInvoice.ServiceUnit.CostRateValue = 60;

			}

			if (String.IsNullOrEmpty(settlementInvoice.InvoiceNumber))
			{
				InvoiceBase ib = null;

				if (settlementInvoice.InvoiceBaseOid.HasValue)
				{
					ib = DAOFactory.GenericDAO.LoadByID<InvoiceBase>(settlementInvoice.InvoiceBaseOid.Value);
				}

				var inc = PluginLoader.FindClass<InvoiceNumberGenerator>();
				settlementInvoice.InvoiceNumber = inc.GetNextInvoiceNumber(0, ib);
			}

			if (periodStart.HasValue && periodEnd.HasValue)
			{
				settlementInvoice.AccountingPeriodStart = periodStart;
				settlementInvoice.AccountingPeriodEnd = periodEnd;
			}
			else
			{
				settlementInvoice.AccountingPeriodStart = c2s.StartDate;
				settlementInvoice.AccountingPeriodEnd = c2s.EndDate;

			}


			CreateSenderInformation(settlementInvoice);
			CreateRecipientInformation(settlementInvoice, c2s);


			if (CanCreateInvoiceTheOldWay(settlementInvoice, c2s))
				CreateInvoiceDetailsTheOldWay(settlementInvoice, c2s);
			else
				CreateInvoiceDetails(settlementInvoice, c2s, periodStart, periodEnd);

			if (settlementInvoice.InvoiceItems == null || settlementInvoice.InvoiceItems.Count == 0)
			{
				settlementInvoice.InvoiceItems = new List<InvoiceItemDC>();
				settlementInvoice.InvoiceItems.Add(
					new InvoiceItemDC
					{
						AmountTotal = settlementInvoice.Claim,
						ItemPeriodStart = settlementInvoice.AccountingPeriodStart,
						ItemPeriodEnd = settlementInvoice.AccountingPeriodEnd,
						ItemDescription = "Spitzabrechnung"
					});
			}
			return settlementInvoice;
		}




		public virtual bool CanCreateInvoiceTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s)
		{
			//Die alte Methode funktioniert nicht mehr wenn
			//1. Es mehr als drei Stundensätze gibt
			//2. Mindestens einem Bewilligungszeitraum eine Kategorie zugeordnet ist
			//3. Wenn sich Bewilligungszeiträume überscheiden
			//4. ServiceRecords einer ServiceDescription/Category mit Stundensätzen zugeordnet sind
			//In allen anderen Fällen wird die alte Methode noch verwendet, das sonst alle Kundenrechnungsformulare angepasst werden müssten.

			//Prüfe 1:
			var relDC = c2s.MapToNewDC();
			var hourlyRatesInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate,
				si.AccountingPeriodStart.Value.Date, si.AccountingPeriodEnd.Value.Date).ToList();
			if (hourlyRatesInSpanList.Count > 3)
				return false;


			//Prüfe 3
			//for (int i = 0; i < c2s.ApprovalPeriodList.Count - 1; i++)
			//{
			//	var ap1 = c2s.ApprovalPeriodList[i];

			//	if (ap1.StartDate.HasValue && ap1.EndDate.HasValue)
			//	{
			//		for (int j = i + 1; j < c2s.ApprovalPeriodList.Count; j++)
			//		{
			//			var ap2 = c2s.ApprovalPeriodList[j];
			//			if (ap2.StartDate.HasValue && ap2.EndDate.HasValue)
			//			{
			//				if (ap1.EndDate >= ap2.StartDate &&  ap1.StartDate <= ap2.EndDate)
			//					return false;
			//			}
			//		}
			//	}
			//}

			//Prüfe 2
			foreach (var ap in c2s.ApprovalPeriodList)
			{
				if (ap.ServiceCategory != null && ap.ServiceCategory.IsActive == ActivationTypeId.Active)
				{
					if (ap.ServiceCategory.CostRatePeriods != null && ap.ServiceCategory.CostRatePeriods.Count > 0)
					{
						foreach (var costRatePeriod in ap.ServiceCategory.CostRatePeriods)
						{
							if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0)
								return false;
						}
					}
					var sdList = DAOFactory.SearchDAO.FindServiceDescriptionForCategory(ap.ServiceCategory.Oid.Value);
					foreach (var sd in sdList)
					{
						if (sd.CostRatePeriods.Count > 0)
						{
							foreach (var costRatePeriod in sd.CostRatePeriods)
							{
								if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0)
									return false;
							}
						}
					}
				}
			}

			//Prüfe 4
			foreach (var sr in c2s.ServiceRecords)
			{
				if (sr.ServiceDescription.CostRatePeriods.Count > 0)
				{
					foreach (var costRatePeriod in sr.ServiceDescription.CostRatePeriods)
					{
						if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0)
							return false;
					}
				}
				if (sr.ServiceDescription.ServiceCategory.CostRatePeriods.Count > 0)
				{
					foreach (var costRatePeriod in sr.ServiceDescription.ServiceCategory.CostRatePeriods)
					{
						if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0)
							return false;
					}
				}
			}
			return true;
		}


		public virtual void CreateInvoiceDetailsTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s)
		{
			bool isLWL = false;
			String id = c2s.CostBearer.ID;
			if (String.IsNullOrEmpty(id))
			{
				if (!String.IsNullOrEmpty(si.RecipientOrganisation) && si.RecipientOrganisation.Trim().ToLower().IndexOf("lwl") == 0)
				{
					isLWL = true;
					id = "LWL";
				}
			
			}
			var calc = PluginLoader.FindClass<Calculations>(id) ?? Calculations.GetInstance(id);
			var relDC = c2s.MapToNewDC();

			decimal lDurationInWeeks =
				calc.GetIntervalCountForDays(
					Convert.ToDecimal((c2s.EndDate.Value - c2s.StartDate.Value).Days + 1),
					SupportConceptApprovalInterval.Weekly);

			decimal lApprovedFLSWeeklyForMaxAmountCalculation = 0;

			CalculateSettlementInvoiceApprovedAmounts(si, relDC, calc);

			DateTime periodStart = si.AccountingPeriodStart.Value.Date;
			DateTime periodEnd = si.AccountingPeriodEnd.Value.Date;

			//Prüfe ob vorher abgemeldet
			if (c2s.SupportConcept.Customer.TerminationDate.HasValue &&
			    c2s.SupportConcept.Customer.TerminationDate.Value < periodEnd)
			{
				periodEnd = c2s.SupportConcept.Customer.TerminationDate.Value;

			}

			var tolerancesInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.ServiceTolerance, periodStart, periodEnd).ToList();

			var hourlyRatesInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate, periodStart, periodEnd).ToList();

			si.DifferentHourlyRateCount = hourlyRatesInSpanList.Count;

			if (hourlyRatesInSpanList.Count > 1)
			{
				if (hourlyRatesInSpanList.Count == 2)
				{
					si.WeeksWithOldHourlyRate =
						calc.GetIntervalCountForDays(Convert.ToDecimal(hourlyRatesInSpanList.First().Value.GetTimeSpan().Days + 1),
							SupportConceptApprovalInterval.Weekly);
					si.WeeksWithNewHourlyRate =
						calc.GetIntervalCountForDays(
							Convert.ToDecimal((c2s.EndDate.Value - hourlyRatesInSpanList.Last().Value.StartDate).Days + 1),
							SupportConceptApprovalInterval.Weekly);
					si.WeeksWithHourlyRate3 = 0;
				}
				else if (hourlyRatesInSpanList.Count == 3)
				{
					si.WeeksWithHourlyRate3 =
						calc.GetIntervalCountForDays(Convert.ToDecimal(hourlyRatesInSpanList.First().Value.GetTimeSpan().Days + 1),
							SupportConceptApprovalInterval.Weekly);
					si.WeeksWithOldHourlyRate =
						calc.GetIntervalCountForDays(Convert.ToDecimal(hourlyRatesInSpanList.ElementAt(1).Value.GetTimeSpan().Days + 1),
							SupportConceptApprovalInterval.Weekly);
					si.WeeksWithNewHourlyRate =
						calc.GetIntervalCountForDays(
							Convert.ToDecimal((c2s.EndDate.Value - hourlyRatesInSpanList.Last().Value.StartDate).Days + 1),
							SupportConceptApprovalInterval.Weekly);
				}
			}
			else
			{
				si.WeeksWithNewHourlyRate = lDurationInWeeks;
			}

			decimal currentRateFactor = 0;
			decimal currentHourlyRate = 0;

			decimal lastHourlyRate = 0;
			decimal thirdHourlyRate = 0;

			var rateFactorsInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, periodStart, periodEnd).ToList();

			if (hourlyRatesInSpanList.Count > 0)
			{
				var cdc = hourlyRatesInSpanList.Last().Key;
				if (cdc.CostRateValue.HasValue)
					currentHourlyRate = cdc.CostRateValue.Value;
			}

			if (rateFactorsInSpanList.Count > 0)
			{
				var cdc = rateFactorsInSpanList.Last().Key;
				if (cdc.CostRateValue.HasValue)
					currentRateFactor = cdc.CostRateValue.Value;
			}


			if (hourlyRatesInSpanList.Count > 1)
			{
				var cdc = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Key;
				if (cdc.CostRateValue.HasValue)
					lastHourlyRate = cdc.CostRateValue.Value;
				si.HourlyRateOldEndDate = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.EndDate;
				si.HourlyRateNewStartDate = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 1].Value.StartDate;
			}

			if (hourlyRatesInSpanList.Count > 2)
			{
				var cdc = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 3].Key;
				if (cdc.CostRateValue.HasValue)
					thirdHourlyRate = cdc.CostRateValue.Value;
				si.HourlyRateEndDate3 = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 3].Value.EndDate;
				si.HourlyRateOldStartDate = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.StartDate;
			}

			if (si.ApprovedFLSWeekly == null)
			{
				si.ApprovedFLSWeekly = 0;
			}


			//decimal maxAmount = 0;

			//decimal maxFLS = lApprovedFLSWeeklyForMaxAmountCalculation * si.WeeksWithNewHourlyRate;
			//maxFLS += lApprovedFLSWeeklyForMaxAmountCalculation * si.WeeksWithOldHourlyRate;
			//maxFLS += lApprovedFLSWeeklyForMaxAmountCalculation * si.WeeksWithHourlyRate3;

			si.RateFactor = currentRateFactor;
			si.HourlyRateNew = currentHourlyRate;

			si.HourlyRateOld = lastHourlyRate;
			si.HourlyRate3 = thirdHourlyRate;

			//decimal totalAmount = 0;


			var records = c2s.ServiceRecords;

			records =
					records.Where(i => i.Start.HasValue && i.Start.Value.InBetween(periodStart, periodEnd, true)).ToList();
			

			var recordDCS = records.MapToNewDCs();

			si.RecordedBillableFLSNotBillableCustomerAbsent
				= calc.GetHoursNotBillableDueToAbsenceTimes(relDC, recordDCS,
					c2s.SupportConcept.Customer.GetAbsencesTimesNotBillableWholeWeeks(), false, false);

			si.RecordedBillableFLSNotBillableOutOfApprovedSpan = calc.GetHoursNotBillableDueToOutOfSpan(relDC, recordDCS);
			//calc.GetHoursNotBillableDueToMoreThanApproved(relDC, recordDCS, false, false);

			Dictionary<long, bool> groupBookingDict = new Dictionary<long, bool>();

			decimal flsCleared = 0;
			records =
				records.OrderBy<ServiceRecord, DateTime>(sr => sr.Start.HasValue ? sr.Start.Value : DateTime.MinValue)
					.ToList<ServiceRecord>();

			decimal tolerance = 100;
			if (tolerancesInSpanList.Count > 0)
			{
				tolerance = tolerancesInSpanList[0].Key.CostRateValue ?? 100;
			}

			decimal approved = si.ApprovedFLS ?? 0;
			if (tolerance != 100)
			{
				approved = (approved*tolerance)/100m;
			}
			foreach (var iRecord in records)
			{
				if (iRecord.ServiceDescription.ServiceCategory.IsBillable)
				{
					if (iRecord.GroupOid == null || !groupBookingDict.ContainsKey(iRecord.GroupOid.Value))
					{
						decimal rd = iRecord.RoundedDuration;
						if (iRecord.ServiceDescription.ServiceCategory.Percentage != 100)
						{
							rd = (rd*iRecord.ServiceDescription.ServiceCategory.Percentage)/100;
						}

						decimal duration = rd/60;
						if (si.RecordedBillableFLSBrutto == null)
						{
							si.RecordedBillableFLSBrutto = 0;
						}

						si.RecordedBillableFLSBrutto += duration;
						si.RecordedBillableFLSNetto += duration;

						
						//if (maxAmount == 0)
						//{
						if (flsCleared + duration > approved)
						{
							duration = approved - flsCleared;
							flsCleared = approved;
						}
						else
						{
							flsCleared += duration;
						}
						//}

						//decimal? hourlyRate = null;
						//decimal? rateFactor = null;

						var rates = c2s.CostBearer.CostRatePeriods.MapToNewDCs();
						//if (iRecord.Start != null)
						//{

						//    hourlyRate = rates.GetValidRateValueForDate(CostRatePeriodType.HourlyRate, iRecord.Start.Value);
						//    rateFactor = rates.GetValidRateValueForDate(CostRatePeriodType.RateFactor, iRecord.Start.Value);

						//}
						//else
						//{
						//    hourlyRate = rates.GetCurrentlyValidRateValue(CostRatePeriodType.HourlyRate);
						//    rateFactor = rates.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor);
						//}
						//if (!hourlyRate.HasValue)
						//    hourlyRate = 0;
						//if (!rateFactor.HasValue)
						//    rateFactor = 0;

						if (hourlyRatesInSpanList.Count > 2 && iRecord.Start != null &&
						    iRecord.Start < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.StartDateTime)
						{
							si.ApprovedFLSWithHourlyRate3 += duration;
						}
						else if (hourlyRatesInSpanList.Count > 1 && iRecord.Start != null &&
						         iRecord.Start < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 1].Value.StartDateTime)
						{
							si.ApprovedFLSWithOldHourlyRate += duration;
						}
						else
						{
							si.ApprovedFLSWithActualHourlyRate += duration;
						}

						if (iRecord.GroupOid != null && !groupBookingDict.ContainsKey(iRecord.GroupOid.Value))
						{
							groupBookingDict.Add(iRecord.GroupOid.Value, true);
						}

					}
				}
			}

			si.ApprovedFLSWithActualHourlyRate = Math.Round(si.ApprovedFLSWithActualHourlyRate, 2, MidpointRounding.AwayFromZero);
			si.ApprovedFLSWithHourlyRate3 = Math.Round(si.ApprovedFLSWithHourlyRate3, 2, MidpointRounding.AwayFromZero);
			si.ApprovedFLSWithOldHourlyRate = Math.Round(si.ApprovedFLSWithOldHourlyRate, 2, MidpointRounding.AwayFromZero);

			decimal rf = 1;
			if (si.RateFactor.HasValue && si.RateFactor.Value != 0)
			{
				rf = si.RateFactor.Value;
				rf = (100 + rf)/100;
			}

			decimal roundedNewAmount = 0;
			decimal amountRateFactor = 0;

			if (si.HourlyRateNew.HasValue)
			{

				roundedNewAmount = Math.Round(si.ApprovedFLSWithActualHourlyRate*si.HourlyRateNew.Value*rf, 2,
					MidpointRounding.AwayFromZero);
				amountRateFactor =
					Math.Round(
						roundedNewAmount -
						Math.Round(si.ApprovedFLSWithActualHourlyRate*si.HourlyRateNew.Value, 2, MidpointRounding.AwayFromZero), 2,
						MidpointRounding.AwayFromZero);
			}
			if (si.HourlyRateOld.HasValue)
			{
				decimal a2 = Math.Round(si.ApprovedFLSWithOldHourlyRate*si.HourlyRateOld.Value*rf, 2, MidpointRounding.AwayFromZero);
				roundedNewAmount += a2;
				amountRateFactor +=
					Math.Round(
						a2 - Math.Round(si.ApprovedFLSWithOldHourlyRate*si.HourlyRateOld.Value, 2, MidpointRounding.AwayFromZero), 2,
						MidpointRounding.AwayFromZero);
			}
			if (si.HourlyRate3 > 0)
			{
				decimal a3 = Math.Round(si.ApprovedFLSWithHourlyRate3*si.HourlyRate3*rf, 2, MidpointRounding.AwayFromZero);
				roundedNewAmount += a3;
				amountRateFactor +=
					Math.Round(a3 - Math.Round(si.ApprovedFLSWithHourlyRate3*si.HourlyRate3, 2, MidpointRounding.AwayFromZero), 2,
						MidpointRounding.AwayFromZero);
			}

			si.AmountWithRateFactor += amountRateFactor;

			var totalAmount = Math.Round(roundedNewAmount, 2, MidpointRounding.AwayFromZero);

			decimal? maxAmount = CalculateMaxAmount(si);

			if (maxAmount.HasValue && totalAmount > maxAmount.Value)
			{
				totalAmount = maxAmount.Value;
			}
			si.AmountBillableTotal = totalAmount;

			//AdvancedPayments
			CalculateAdvancedPayments(si, c2s, null, null, hourlyRatesInSpanList);
			//EquityContribution (Eigenanteil)
			CalculateEquityContribution(si, c2s.SupportConcept, null, null);

			si.Claim = si.AmountBillableTotal - (si.AmountAdvancedPayments ?? 0) - si.EquityContribution;

		}

		public virtual decimal? CalculateMaxAmount(Settlement2DC si)
		{
			return null;
		}

		public virtual void CreateInvoiceDetails(Settlement2DC si, CostBearer2SupportConcept c2s, DateTime? periodStart,
			DateTime? periodEnd)
		{
			var calc = PluginLoader.FindClass<Calculations>(_SpecificID) ?? Calculations.GetInstance(_SpecificID);

			var records = c2s.ServiceRecords;

			DateTimeSpan span = null;
			if (periodStart.HasValue && periodEnd.HasValue)
			{
				span = new DateTimeSpan();
				span.StartDateTime = periodStart.Value;
				span.EndDateTime = periodEnd.Value;

				records =
					records.Where(
						i => i.Start.HasValue && i.Start.Value.InBetween(periodStart.Value, periodEnd.Value, true))
						.ToList();
			}

			var recordDCS = records.MapToNewDCsCompact();
			var relDC = c2s.MapToNewDC();

			CalculateSettlementInvoiceApprovedAmounts(si, relDC, calc);

			var accountingPeriods = calc.GetAccountingPeriods(relDC, recordDCS, span);

			si.InvoiceItems = CreateInvoiceItemsForAccountingPeriods(accountingPeriods, relDC.CostBearer.CostRatePeriods, calc);

			//CalculateMaxApprovedAmounts(accountingPeriods, relDC.CostBearer.CostRatePeriods, calc);

			CalculateAdvancedPayments(si, c2s);

			CalculateEquityContribution(si, c2s.SupportConcept, periodStart, periodEnd);

			CalculateSettlementInvoiceAmounts(si);
		}

		//public virtual void CalculateMaxApprovedAmounts(IList<AccountingPeriod> accountingPeriods, List<CostRatePeriodDC> costRatePeriods, Calculations calc)
		//{
		//	Dictionary<long, SupportConceptApprovalPeriodDC> oid2Scap = new Dictionary<long, SupportConceptApprovalPeriodDC>();
		//	foreach (var ap in accountingPeriods)
		//	{

		//		var scap = ap.SupportConceptApprovalPeriod;
		//		SupportConceptApprovalPeriodDC scapHelper = null;

		//		if (scap != null)
		//		{
		//			if (oid2Scap.ContainsKey(scap.SupportConceptApprovalPeriodOid.Value))
		//				scapHelper = oid2Scap[scap.SupportConceptApprovalPeriodOid.Value];
		//			else
		//			{
		//				scapHelper = new SupportConceptApprovalPeriodDC();
		//				scapHelper.ApprovedBETotal = scap.ApprovedBETotal;
		//				if (!scapHelper.ApprovedBETotal.HasValue)
		//					scapHelper.ApprovedBETotal = calc.GetApprovedBETotal(scap, costRatePeriods);
		//			}
		//		}

		//		if (scapHelper.ApprovedBETotal.HasValue && scapHelper.ApprovedBETotal > ap.co


		//	}


		//}


		public virtual void CalculateSettlementInvoiceAmounts(Settlement2DC si)
		{
			decimal maxAmount = 0;
			if (si.InvoiceItems != null)
			{
				si.AmountBillableTotal = si.InvoiceItems.Sum(ii => ii.GrossAmountTotal) ?? 0;

				//maxAmount = si.InvoiceItems.Sum(ii =>
				//    {
				//        decimal amount = ii.GrossAmountTotal ?? 0;
				//        if (ii.MaxApprovedAmount.HasValue && ii.MaxApprovedAmount.Value > 0 && ii.MaxApprovedAmount < amount)
				//            amount = ii.MaxApprovedAmount.Value;
				//        return amount;
				//    });
				if (maxAmount == 0)
					maxAmount = si.AmountBillableTotal.Value;
			}

			si.Claim = maxAmount - (si.AmountAdvancedPayments ?? 0) - (si.EquityContribution ?? 0);
		}

		public virtual List<InvoiceItemDC> CreateInvoiceItemsForAccountingPeriods(List<AccountingPeriod> accountingPeriods,
			List<CostRatePeriodDC> costRatePeriods, Calculations calc)
		{
			Dictionary<long, SupportConceptApprovalPeriodDC> oid2Scap = new Dictionary<long, SupportConceptApprovalPeriodDC>();
			List<InvoiceItemDC> invoiceItems = new List<InvoiceItemDC>();

			foreach (var ap in accountingPeriods)
			{
				InvoiceItemDC ii = CreateInvoiceItemForAccountingPeriod(ap);
				invoiceItems.Add(ii);

				var scap = ap.SupportConceptApprovalPeriod;
				SupportConceptApprovalPeriodDC scapHelper = null;

				if (scap != null)
				{
					if (oid2Scap.ContainsKey(scap.SupportConceptApprovalPeriodOid.Value))
						scapHelper = oid2Scap[scap.SupportConceptApprovalPeriodOid.Value];
					else
					{
						scapHelper = new SupportConceptApprovalPeriodDC();
						scapHelper.ApprovedBETotal = scap.ApprovedBETotal;
						if (!scapHelper.ApprovedBETotal.HasValue)
							scapHelper.ApprovedBETotal = calc.GetApprovedBETotal(scap, costRatePeriods);

						oid2Scap[scap.SupportConceptApprovalPeriodOid.Value] = scapHelper;
					}
				}

				if (scapHelper.ApprovedBETotal.HasValue && ii.UnitCount.HasValue)
				{
					if (scapHelper.ApprovedBETotal >= ii.UnitCount.Value)
					{
						scapHelper.ApprovedBETotal -= ii.UnitCount.Value;
						ii.MaxApprovedUnitCount = null;
					}
					else
					{
						ii.MaxApprovedUnitCount = scapHelper.ApprovedBETotal;
					}
				}

				CalculateInvoiceItemAmounts(ii);
				var list = CreateAdditionalInvoiceItems(ap);
				invoiceItems.AddRange(list);
			}
			return invoiceItems;
		}

		public virtual IList<InvoiceItemDC> CreateAdditionalInvoiceItems(AccountingPeriod ap)
		{
			//only for subclasses
			return new List<InvoiceItemDC>();
		}

		public virtual InvoiceItemDC CreateInvoiceItemForAccountingPeriod(AccountingPeriod accountingPeriod)
		{
			InvoiceItemDC ii = new InvoiceItemDC();
			ii.AccountingInterval = accountingPeriod.AccountingInterval;
			ii.ItemPeriodStart = accountingPeriod.PeriodStart;
			ii.ItemPeriodEnd = accountingPeriod.PeriodEnd;
			ii.RateFactor = accountingPeriod.RateFactor;
			ii.AmountPerUnit = accountingPeriod.Amount;
			ii.UnitDescription = accountingPeriod.UnitName;
			ii.MaxApprovedAmount = accountingPeriod.MaxApprovedAmount;
			ii.MaxApprovedUnitCount = accountingPeriod.MaxApprovedUnitCount;
			ii.ApprovedPerUnit = accountingPeriod.ApprovedPerUnit;
			ii.ApprovalUnit = accountingPeriod.ApprovalUnit;


			if (accountingPeriod.ServiceCategory != null)
				ii.ItemDescription = accountingPeriod.ServiceCategory.Name;

			if (accountingPeriod.AccountingInterval == AccountingIntervalType.FlatRate)
			{
				ii.UnitCount = accountingPeriod.ServiceRecordsInPeriod.Count;

			}
			else if (accountingPeriod.AccountingInterval == AccountingIntervalType.Hourly)
			{
				decimal minutesTotal = 0;

				if (accountingPeriod.ServiceRecordsInPeriod != null)
				{
					foreach (var ar in accountingPeriod.ServiceRecordsInPeriod)
					{
						var rd = ar.RoundedDuration;
						if (ar.ServiceDescription.Category.Percentage != 100)
						{
							rd = (rd*ar.ServiceDescription.Category.Percentage)/100;
						}
						minutesTotal += rd;

					}
				}
				ii.UnitCount = minutesTotal/60;

				if (String.IsNullOrEmpty(ii.UnitDescription))
					ii.UnitDescription = ii.UnitCount.Value == 1 ? "Stunde" : "Stunden";
			}
			else if (accountingPeriod.PeriodStart > DateTime.MinValue && accountingPeriod.PeriodEnd < DateTime.MaxValue)
			{
				DateTime intervalStart = accountingPeriod.PeriodStart;
				DateTime intervalEnd = intervalStart;
				int count = 0;
				while (intervalStart < accountingPeriod.PeriodEnd)
				{
					count++;
					switch (accountingPeriod.AccountingInterval)
					{
						case AccountingIntervalType.Daily:
							intervalEnd = intervalStart.AddDays(1);
							break;
						case AccountingIntervalType.Weekly:
							intervalEnd = intervalStart.AddDays(7);
							break;
						case AccountingIntervalType.Fortnightly:
							intervalEnd = intervalStart.AddDays(14);
							break;
						case AccountingIntervalType.Monthly:
							intervalEnd = intervalStart.AddMonths(1);
							break;
						case AccountingIntervalType.Quarterly:
							intervalEnd = intervalStart.AddMonths(3);
							break;
						case AccountingIntervalType.HalfYearly:
							intervalEnd = intervalStart.AddMonths(6);
							break;
						case AccountingIntervalType.Yearly:
							intervalEnd = intervalStart.AddYears(1);
							break;
					}
					intervalStart = intervalEnd;
				}
				ii.UnitCount = count;

				switch (accountingPeriod.AccountingInterval)
				{
					case AccountingIntervalType.Daily:
						ii.UnitDescription = count == 1 ? "Tag" : "Tage";
						break;
					case AccountingIntervalType.Weekly:
						ii.UnitDescription = count == 1 ? "Woche" : "Wochen";
						break;
					case AccountingIntervalType.Fortnightly:
						ii.UnitDescription = "2 Wochen";
						break;
					case AccountingIntervalType.Monthly:
						ii.UnitDescription = count == 1 ? "Monat" : "Monate";
						break;
					case AccountingIntervalType.Quarterly:
						ii.UnitDescription = count == 1 ? "Quartal" : "Quartale";
						break;
					case AccountingIntervalType.HalfYearly:
						ii.UnitDescription = "Halbjährlich";
						break;
					case AccountingIntervalType.Yearly:
						ii.UnitDescription = count == 1 ? "Jahr" : "Jahre";
						break;
				}
			}


			return ii;
		}

		public virtual void CalculateInvoiceItemAmounts(InvoiceItemDC ii)
		{
			if (ii.UnitCount.HasValue)
			{
				var uc = ii.UnitCount.Value;
				if (ii.MaxApprovedUnitCount.HasValue && ii.MaxApprovedUnitCount.Value < uc)
					uc = ii.MaxApprovedUnitCount.Value;
				ii.UnitCount = Math.Round(uc, 2, MidpointRounding.AwayFromZero);
				if (ii.AmountPerUnit.HasValue)
				{
					ii.AmountTotal = Math.Round(ii.UnitCount.Value*ii.AmountPerUnit.Value, 2,
						MidpointRounding.AwayFromZero);
					if (ii.RateFactor.HasValue && ii.RateFactor.Value != 0)
						ii.GrossAmountTotal = Math.Round(ii.AmountTotal.Value*((100m + ii.RateFactor.Value)/100m), 2,
							MidpointRounding.AwayFromZero);
					else
						ii.GrossAmountTotal = ii.AmountTotal;
				}
			}
		}

		public virtual void CalculateSettlementInvoiceApprovedAmounts(Settlement2DC si, SupportConceptCostBearerRelDC relDC,
			Calculations calc)
		{
			decimal flsWeek = calc.GetApprovedHoursPerWeekAverage(relDC, true) ?? 0m;
			decimal flsTotal = calc.GetApprovedHoursTotal(relDC, true) ?? 0m;
			flsTotal = Math.Round(flsTotal, 2, MidpointRounding.AwayFromZero);
			si.ApprovedFLS = flsTotal;
			si.ApprovedFLSWeekly = flsWeek;
		}



		public virtual void CreateRecipientInformation(Settlement2DC settlementInvoice, CostBearer2SupportConcept c2s)
		{
			if (c2s.CostBearer != null && c2s.CostBearer.Organisation != null)
			{
				settlementInvoice.RecipientDivision = "";
				settlementInvoice.RecipientOrganisation = c2s.CostBearer.Organisation.Name;
			}

			if (c2s.CostBearerContactPerson != null)
			{
				settlementInvoice.RecipientContactPerson = c2s.CostBearerContactPerson.ToString();
			}
			else if (c2s.CostBearer != null && c2s.CostBearer.Organisation != null)
			{
				var lContactPerson = c2s.CostBearer.Organisation.Contacts.SingleOrDefault(c => c.Type == ContactType.ContactPerson);
				if (lContactPerson != null)
				{
					settlementInvoice.RecipientContactPerson = lContactPerson.Value;
				}
			}

            // War die private Rechnungsadresse der Kontaktperson
            //if (c2s.CostBearerContactPerson != null && c2s.CostBearerContactPerson.InvoiceAddress != null)
            //{
            //    settlementInvoice.RecipientPostCode = c2s.CostBearerContactPerson.InvoiceAddress.PostalCode;
            //    settlementInvoice.RecipientStreet = c2s.CostBearerContactPerson.InvoiceAddress.Street;
            //    settlementInvoice.RecipientTown = c2s.CostBearerContactPerson.InvoiceAddress.Town;
            //}
            //else 
            if (c2s.CostBearer != null && c2s.CostBearer.Organisation != null)
			{
				if (c2s.CostBearer.Organisation.InvoiceAddress != null &&
				    !String.IsNullOrEmpty(c2s.CostBearer.Organisation.InvoiceAddress.Street))
				{
					settlementInvoice.RecipientPostCode = c2s.CostBearer.Organisation.InvoiceAddress.PostalCode;
					settlementInvoice.RecipientStreet = c2s.CostBearer.Organisation.InvoiceAddress.Street;
					settlementInvoice.RecipientTown = c2s.CostBearer.Organisation.InvoiceAddress.Town;
				}
				else if (c2s.CostBearer.Organisation.Address != null)
				{
					settlementInvoice.RecipientPostCode = c2s.CostBearer.Organisation.Address.PostalCode;
					settlementInvoice.RecipientStreet = c2s.CostBearer.Organisation.Address.Street;
					settlementInvoice.RecipientTown = c2s.CostBearer.Organisation.Address.Town;
				}

                settlementInvoice.RecipientPostBox = c2s.CostBearer.Organisation.Contacts.FirstOrDefault(f => f.Type.Equals(ContactType.business_POBox)) != null ? c2s.CostBearer.Organisation.Contacts.FirstOrDefault(f => f.Type.Equals(ContactType.business_POBox)).Value : String.Empty;
			}
		}

		public virtual void CreateSenderInformation(Settlement2DC settlementInvoice)
		{
			if (LoggedInUserOperationContextExt.Current != null)
			{
				var lLoggedInEmployee = LoggedInUserOperationContextExt.Current.User.Employee;


				if (lLoggedInEmployee != null)
				{
					settlementInvoice.SenderContactPersonFirstName = lLoggedInEmployee.Person.FirstName;
					settlementInvoice.SenderContactPersonLastName = lLoggedInEmployee.Person.LastName;

					foreach (var iContact in lLoggedInEmployee.Person.Contacts)
					{
						switch (iContact.Type)
						{
							case ContactType.business_Fax:
								settlementInvoice.SenderContactPersonFax = iContact.Value;
								break;
							case ContactType.business_Mail:
								settlementInvoice.SenderContactPersonMail = iContact.Value;
								break;
							case ContactType.business_Phone:
								settlementInvoice.SenderContactPersonPhone = iContact.Value;
								break;
						}
					}

					settlementInvoice.CreatorFirstName = lLoggedInEmployee.Person.FirstName;
					settlementInvoice.CreatorLastName = lLoggedInEmployee.Person.LastName;
				}
			}
		}

		public virtual void CalculateAdvancedPayments(Settlement2DC settlementInvoice, CostBearer2SupportConcept cb2sc)
		{
			if (settlementInvoice.InvoiceItems != null && settlementInvoice.InvoiceItems.Count > 0)
			{
				decimal amountTotal = 0;
				var bookingList = cb2sc.AccountingTransactions;

				foreach (var item in bookingList)
				{
					if (item.BookingDate.HasValue)
					{
						foreach (var ii in settlementInvoice.InvoiceItems)
						{
							if (ii.ItemPeriodStart.HasValue && ii.ItemPeriodEnd.HasValue)
							{
								if (item.BookingDate.Value.InBetween(ii.ItemPeriodStart.Value, ii.ItemPeriodEnd.Value, true))
								{
									ii.ReceivedAmount += item.Amount;
									
									break;
								}
							}
						}
					}
					amountTotal += item.Amount;
				}

				settlementInvoice.AmountAdvancedPayments = amountTotal;
			}
		}

		public virtual void CalculateAdvancedPayments(Settlement2DC settlementInvoice, CostBearer2SupportConcept cb2sc,
			DateTime? periodStart, DateTime? periodEnd, List<KeyValuePair<CostRatePeriodDC, DateTimeSpan>> hourlyRatesInSpanList)
		{
			decimal amountNew = 0;
			decimal amountOld = 0;
			decimal amount3 = 0;
			decimal amountTotal = 0;

			var bookingList = cb2sc.AccountingTransactions;

			if (periodStart.HasValue && periodEnd.HasValue)
			{
				bookingList =
					bookingList.Where(i => i.BookingDate.HasValue
					                       && i.BookingDate.Value.InBetween(periodStart.Value, periodEnd.Value, true)).ToList();
			}

			foreach (var item in bookingList)
			{
				if (hourlyRatesInSpanList.Count > 2 && item.BookingDate != null &&
				    item.BookingDate < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.StartDateTime)
				{
					amount3 += item.Amount;
				}
				else if (hourlyRatesInSpanList.Count > 1 && item.BookingDate != null &&
				         item.BookingDate < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 1].Value.StartDateTime)
				{
					amountOld += item.Amount;
				}
				else
				{
					amountNew += item.Amount;
				}

				amountTotal += item.Amount;
			}

			settlementInvoice.AmountTotalAdvancedPaidWithNewHourlyRate = amountNew;
			settlementInvoice.AmountTotalAdvancedPaidWithOldHourlyRate = amountOld;
			settlementInvoice.AmountTotalAdvancedPaidWithHourlyRate3 = amount3;
			settlementInvoice.AmountAdvancedPayments = amountTotal;
		}

		public virtual void CalculateEquityContribution(Settlement2DC settlementInvoice, SupportConcept sc,
			DateTime? periodStart, DateTime? periodEnd)
		{
			decimal equity = 0;

			IList<InvoiceBase> invoiceList =
				DAOFactory.SearchDAO.GetInvoiceBaseByTypeAndSupportConceptOid(InvoiceType.CustomerEquity, sc.Oid.Value);

			if (periodStart.HasValue && periodEnd.HasValue)
			{
				invoiceList =
					invoiceList.Where(i => i.InvoiceDate.HasValue
					                       && i.InvoiceDate.Value.InBetween(periodStart.Value, periodEnd.Value, true)).ToList();
			}

			foreach (InvoiceBase invoice in invoiceList)
			{
				foreach (InvoiceItem item in invoice.InvoiceItems)
				{
					if (item.AmountTotal != null)
					{
						equity += item.AmountTotal.Value;
					}
				}
			}

			settlementInvoice.EquityContribution = equity;
		}

		public virtual IList<ApprovalPeriod> CreateApprovalPeriods(Settlement2DC si, CostBearer2SupportConcept c2s)
		{
			var relDc = c2s.MapToNewDC();

			var hourlyRatesInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate,
				si.AccountingPeriodStart.Value.Date, si.AccountingPeriodEnd.Value.Date).ToList();

			Dictionary<DateTime, ApprovalPeriod> dtApDict = new Dictionary<DateTime, ApprovalPeriod>();
			foreach (var keyValuePair in hourlyRatesInSpanList)
			{
				var ap = new ApprovalPeriod();
				ap.PeriodStartDate = keyValuePair.Value.StartDate;
				ap.PeriodEndDate = DateTime.MaxValue;
				dtApDict.Add(ap.PeriodStartDate, ap);
			}

			var rateFactorInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor,
				si.AccountingPeriodStart.Value.Date, si.AccountingPeriodEnd.Value.Date).ToList();

			foreach (var keyValuePair in rateFactorInSpanList)
			{
				if (!dtApDict.ContainsKey(keyValuePair.Value.StartDate))
				{
					var ap = new ApprovalPeriod();
					ap.PeriodStartDate = keyValuePair.Value.StartDate;
					ap.PeriodEndDate = DateTime.MaxValue;
					dtApDict.Add(ap.PeriodStartDate, ap);
				}
			}

			foreach (var scap in c2s.ApprovalPeriodList)
			{
				if (scap.StartDate.HasValue && scap.EndDate.HasValue)
				{
					if (!dtApDict.ContainsKey(scap.StartDate.Value))
					{
						if ((scap.ApprovedBEInterval.HasValue && scap.ApprovedBEPerInterval.HasValue) || scap.ApprovedBETotal.HasValue)
						{
							var ap = new ApprovalPeriod();
							ap.PeriodStartDate = scap.StartDate.Value;
							ap.PeriodEndDate = DateTime.MaxValue;
							dtApDict.Add(ap.PeriodStartDate, ap);
						}
					}
				}
			}

			foreach (var dt in dtApDict.Keys)
			{
				ApprovalPeriod ap = dtApDict[dt];
				var hrspan = hourlyRatesInSpanList.FirstOrDefault(ispan => ispan.Value.StartDate <= dt && dt <= ispan.Value.EndDate);
				var rfspan = rateFactorInSpanList.FirstOrDefault(ispan => ispan.Value.StartDate <= dt && dt <= ispan.Value.EndDate);

				var scap = c2s.ApprovalPeriodList.FirstOrDefault(iscap => iscap.StartDate <= dt && dt <= iscap.EndDate);

				if (hrspan.Key != null && hrspan.Key.CostRateValue.HasValue)
				{
					ap.AmountPerUnit = hrspan.Key.CostRateValue.Value;
					ap.BillingInterval = AccountingIntervalType.Hourly;
				}
				if (hrspan.Value != null && hrspan.Value.EndDate < ap.PeriodEndDate)
				{
					ap.PeriodEndDate = hrspan.Value.EndDate;
				}
				if (rfspan.Key != null && rfspan.Key.CostRateValue.HasValue)
				{
					ap.RateFactor = rfspan.Key.CostRateValue.Value;
				}
				if (rfspan.Value != null && rfspan.Value.EndDate < ap.PeriodEndDate)
				{
					ap.PeriodEndDate = rfspan.Value.EndDate;
				}
				if (scap != null)
				{
					if (scap.EndDate.HasValue && scap.EndDate < ap.PeriodEndDate)
						ap.PeriodEndDate = scap.EndDate.Value;

					CalculateApprovedAmountForApprovalPeriod(ap, scap);				
				}
			}


			var list = dtApDict.Values.OrderBy(ap => ap.PeriodStartDate).ToList();

			foreach (var approvalPeriod in list)
			{
				InitApprovalPeriod(si, approvalPeriod);
			}
			return list;
		}

		public virtual void CalculateApprovedAmountForApprovalPeriod(ApprovalPeriod ap, SupportConceptApprovalPeriod scap)
		{
			if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue)
			{
				ap.ApprovedPerUnit = scap.ApprovedBEPerInterval.Value;
				ap.ApprovalUnit = Calculations.ConvertApprovalToAccountingIntervalType(scap.ApprovedBEInterval.Value);
			}
			else if (scap.ApprovedBETotal.HasValue)
			{
				//Gesamtstunden auf ap Zeitraum umrechnen 
				if (ap.PeriodStartDate == scap.StartDate && ap.PeriodEndDate == scap.EndDate)
				{
					ap.ApprovedPerUnit = scap.ApprovedBETotal.Value;
				}
				else if (scap.EndDate.HasValue && scap.StartDate.HasValue)
				{
					decimal perioddays = (decimal)ap.PeriodEndDate.Subtract(ap.PeriodStartDate).TotalDays + 1;
					decimal totaldays = (decimal)scap.EndDate.Value.Subtract(scap.StartDate.Value).TotalDays + 1;

					var approvedPerDay = scap.ApprovedBETotal.Value / totaldays;
					ap.ApprovedPerUnit = perioddays * approvedPerDay;

				}
				else
				{
					ap.ApprovedPerUnit = scap.ApprovedBETotal.Value;
				}
				ap.ApprovalUnit = AccountingIntervalType.FlatRate;
			}

		}

		public virtual void InitApprovalPeriod(Settlement2DC si, ApprovalPeriod approvalPeriod)
		{
			approvalPeriod.Days = approvalPeriod.PeriodEndDate.Subtract(approvalPeriod.PeriodStartDate).Days + 1;
			approvalPeriod.Weeks = approvalPeriod.Days / 7m;

			if (approvalPeriod.ApprovalUnit == AccountingIntervalType.FlatRate)
			{
				approvalPeriod.ApprovedTotal = approvalPeriod.ApprovedPerUnit;
			}
			else
			{
				
				approvalPeriod.ApprovedTotal = Math.Round(approvalPeriod.Weeks * approvalPeriod.ApprovedPerUnit, 2,
					MidpointRounding.AwayFromZero);
			}
			approvalPeriod.ApprovedAmountTotal = Math.Round(approvalPeriod.ApprovedTotal * approvalPeriod.AmountPerUnit, 2,
					MidpointRounding.AwayFromZero);
		}
	}
}
