using System;
using System.Collections.Generic;
using System.Linq;

using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.Extensions;

namespace BS.Shared.Services
{
    public class Calculations : AbstractIDSpecificDefaultClass<Calculations>
    {
        #region Public Methods

        public virtual decimal GetIntervalCountForDays(decimal totalDays, SupportConceptApprovalInterval interval)
        {
            return ConvertPerIntervall2PerDay(totalDays, interval);
        }

        public virtual decimal ConvertPerIntervall2PerDay(decimal pPerInterval, SupportConceptApprovalInterval pInterval)
        {
            switch (pInterval)
            {
                case SupportConceptApprovalInterval.Daily:
                    return pPerInterval;
                case SupportConceptApprovalInterval.Weekly:
                    return pPerInterval / 7m;
                case SupportConceptApprovalInterval.Fortnightly:
                    return pPerInterval / 14m;
                case SupportConceptApprovalInterval.Monthly:
                    return pPerInterval / (365m / 12m);
                case SupportConceptApprovalInterval.Quarterly:
                    return pPerInterval / (365m / 4m);
                case SupportConceptApprovalInterval.HalfYearly:
                    return pPerInterval / (365m / 2m);
                case SupportConceptApprovalInterval.Yearly:
                    return pPerInterval / 365m;
                default:
                    return 0;
            }
        }

        public virtual decimal? GetApprovedAmount(SupportConceptApprovalPeriodDC scap, List<CostRatePeriodDC> costRatePeriods)
        {
	        decimal t1;
	        decimal t2;

            return scap.ApprovedFixedAmount.HasValue
                       ? this.GetApprovedFixedAmount(scap, out t1 , out t2)
                       : this.GetApprovedAmountDefaultHourlyRate(scap, costRatePeriods);
        }

        public virtual decimal? GetApprovedAmountDefaultHourlyRate(SupportConceptApprovalPeriodDC scap, List<CostRatePeriodDC> costRatePeriods)
        {
            return this.GetApprovedAmountDefaultHourlyRateForPeriod(scap, costRatePeriods, scap.StartDate, scap.EndDate);
        }

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

			if (scap.ApprovedBEInterval.HasValue && scap.ApprovedBEPerInterval.HasValue)
			{
				if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Monthly && !scap.IsApprovedBEShifting)
				{
					var totalMonths = GetTotalMonths(start.Value, end.Value);
					var approvedHours = totalMonths*scap.ApprovedBEPerInterval.Value;

					
					var hourlyRate = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, start.Value);
					var rateFactor = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, start.Value);
					decimal amount = 0;
					if (hourlyRate != null && hourlyRate.CostRateValue.HasValue)
						amount = approvedHours * hourlyRate.CostRateValue.Value;
					if (rateFactor != null && rateFactor.CostRateValue.HasValue)
					{
						amount = amount + (amount * rateFactor.CostRateValue.Value / 100);
					}

					return amount;
				}
			}


            var approvedHoursPerDay = this.GetApprovedHoursPerDay(scap, costRatePeriods);
            if (!approvedHoursPerDay.HasValue)
            {
                return null;
            }

            decimal? result = 0m;

            var day = start.Value.Date;
            do
            {
                var hourlyRate = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, day);
                var rateFactor = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, day);
                decimal? amountThisDay = 0;

                if (hourlyRate != null && hourlyRate.CostRateValue.HasValue && approvedHoursPerDay.HasValue)
                    amountThisDay = approvedHoursPerDay.Value * hourlyRate.CostRateValue.Value;
                if (rateFactor != null && rateFactor.CostRateValue.HasValue)
                {
                    amountThisDay = amountThisDay + (amountThisDay * rateFactor.CostRateValue / 100);
                }

                result += amountThisDay;
            }
            while ((day = day.AddDays(1)) <= end.Value.Date);

            return result;
        }

        public virtual decimal? GetApprovedAmountPerMonthAverage(SupportConceptCostBearerRelDC relDC)
        {
            if (relDC.MonthlyPayment.HasValue)
            {
                return relDC.MonthlyPayment;
            }

            if (relDC.ApprovedStartDate.HasValue && relDC.ApprovedEndDate.HasValue)
            {
                return this.GetApprovedAmountTotal(relDC) / this.GetMonthCount(relDC.ApprovedStartDate.Value, relDC.ApprovedEndDate.Value);
            }

            return 0;
        }

        public virtual decimal? GetApprovedAmountPerMonth(SupportConceptCostBearerRelDC relDC, DateTime dt)
        {
            if (relDC.MonthlyPayment.HasValue)
            {
                return relDC.MonthlyPayment;
            }

            var scap = FindApprovalPeriodForDateAndCategory(relDC, dt, null);
            if (scap == null)
            {
                //letze Periode nehmen
                if (relDC.ApprovalPeriodList.Count > 0)
                    scap = relDC.ApprovalPeriodList[0];

                foreach (var ap in relDC.ApprovalPeriodList)
                {
                    if (!ap.EndDate.HasValue)
                        scap = ap;
                    else if (scap.EndDate.HasValue && ap.EndDate > scap.EndDate)
                        scap = ap;
                }

            }
            if (scap != null && scap.MonthlyPayment.HasValue && scap.MonthlyPayment.Value != 0)
                return scap.MonthlyPayment;
            else
            {
                if (relDC.ApprovedStartDate.HasValue && relDC.ApprovedEndDate.HasValue)
                {
                    decimal ratefactor = 1;
                    if (relDC.CostBearer != null && relDC.CostBearer.CostRatePeriods != null)
                    {
                        decimal? factor =
                            relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor);
                        if (factor.HasValue)
                            ratefactor = (100 + factor.Value) / 100;
                    }


                    return (this.GetApprovedAmountTotal(relDC) / this.GetMonthCount(relDC.ApprovedStartDate.Value, relDC.ApprovedEndDate.Value)) / ratefactor;
                }
            }
            return 0;
        }

        public virtual SupportConceptApprovalPeriodDC FindApprovalPeriodForDateAndCategory(SupportConceptCostBearerRelDC relDC, DateTime dt, ServiceCategoryDC category)
        {
            SupportConceptApprovalPeriodDC scap = null;

            if (category != null)
                scap = relDC.ApprovalPeriodList.FirstOrDefault(
                    i =>
                    dt.InBetween(i.Span, true) && i.ServiceCategory != null &&
                    i.ServiceCategory.ServiceCategoryOid == category.ServiceCategoryOid);

            if (scap == null)
                scap = relDC.ApprovalPeriodList.FirstOrDefault(i => dt.InBetween(i.Span, true));

            return scap;
        }

        public virtual decimal? GetApprovedAmountTotal(SupportConceptCostBearerRelDC relDC)
        {
            if (relDC.ApprovalPeriodList == null || !relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }

            return relDC.ApprovalPeriodList.Sum(i => this.GetApprovedAmount(i, relDC.CostBearer.CostRatePeriods));
        }

        public virtual decimal? GetApprovedBEPerWeek(SupportConceptApprovalPeriodDC scap,
                                                     List<CostRatePeriodDC> costRatePeriods)
        {
            return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursPerWeek(scap, costRatePeriods));
        }

        public virtual decimal? GetApprovedBEPerWeek(SupportConceptCostBearerRelDC relDC,
                                                     DateTime date)
        {
            if (!relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }

            return relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursPerWeek(relDC, date));
        }

        public virtual decimal? GetApprovedBEPerWeekAverage(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor)
        {
            if (!relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }

            return relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursPerWeekAverage(relDC, useIsCalculationWithFactor));
        }

        private decimal? GetApprovedBETotal(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor)
        {
            if (!relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }

            return relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursTotal(relDC, useIsCalculationWithFactor));
        }

        public virtual decimal? GetApprovedBETotal(SupportConceptApprovalPeriodDC scap,
                                                   List<CostRatePeriodDC> costRatePeriods)
        {
            return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursTotal(scap, costRatePeriods));
        }

		public virtual decimal? GetApprovedFixedAmount(SupportConceptApprovalPeriodDC scap, out decimal amountPerUnit, out decimal unitCount)
        {
            return this.GetApprovedFixedAmountForPeriod(scap, scap.StartDate, scap.EndDate, out amountPerUnit, out unitCount);
        }

        public virtual decimal? GetApprovedFixedAmountForPeriod(SupportConceptApprovalPeriodDC scap, DateTime? pStart, DateTime? pEnd, out decimal amountPerUnit, out decimal unitCount)
        {
	        amountPerUnit = 0;
	        unitCount = 0;

            if (!pStart.HasValue || !pEnd.HasValue || !scap.ApprovedFixedAmount.HasValue || !scap.ApprovedFixedAmountInterval.HasValue)
            {
                return null;
            }
            decimal amount = 0;


            TimeUnit tu = TimeUnit.Day;
            switch (scap.ApprovedFixedAmountInterval.Value)
            {

                case SupportConceptApprovalInterval.Weekly:
                    tu = TimeUnit.Week;
                    break;

                case SupportConceptApprovalInterval.Fortnightly:
                    tu = TimeUnit.Fortnightly;
                    break;

                case SupportConceptApprovalInterval.Monthly:
                    tu = TimeUnit.Month;
                    break;
                case SupportConceptApprovalInterval.Quarterly:

                    tu = TimeUnit.Quarter;
                    break;
                case SupportConceptApprovalInterval.HalfYearly:
                    tu = TimeUnit.Halfyear;
                    break;

                case SupportConceptApprovalInterval.Yearly:
                    tu = TimeUnit.Year;
                    break;

            }

            DateTimeUnit du = pStart.Value.Date.GetDateTimeUnit(pEnd.Value.Date, tu);
            amount = scap.ApprovedFixedAmount.Value * du.UnitCount;

	        amountPerUnit = scap.ApprovedFixedAmount.Value;
	        unitCount = du.UnitCount;

            if (du.RestDays > 0)
            {
                decimal restamountperday = ConvertPerIntervall2PerDay(scap.ApprovedFixedAmount.Value, scap.ApprovedFixedAmountInterval.Value);

                amount += (restamountperday * du.RestDays);
            }
            return amount;
        }

        public virtual decimal GetApprovedHoursForPeriod(SupportConceptCostBearerRelDC relDC,
                                                          DateTime pStart,
                                                          DateTime pEnd)
        {

            var costRatePeriods = relDC.CostBearer.CostRatePeriods;

            decimal approvedHours = 0;

            DateTime start = pStart;
            DateTime end = pEnd;

            while (start <= end)
            {
                var scap = relDC.ApprovalPeriodList.FirstOrDefault(i => start.InBetween(i.Span, true));

                if (scap != null)
                {
                    if (scap.EndDate.HasValue && scap.EndDate.Value < end)
                        end = scap.EndDate.Value;

                    var periodInDays = end.Subtract(start).Days + 1;

                    approvedHours += (this.GetApprovedHoursPerDay(scap, costRatePeriods) ?? 0) * periodInDays;
                }
                else
                {
                    DateTime minStart = DateTime.MaxValue;

                    foreach (var period in relDC.ApprovalPeriodList)
                    {
                        if (period.StartDate.HasValue && period.EndDate.HasValue)
                        {
                            if (period.StartDate.Value > start && period.StartDate.Value <= pEnd && period.StartDate.Value < minStart)
                            {
                                minStart = period.StartDate.Value;
                            }
                        }
                    }
                    end = minStart.AddDays(-1);
                }

                start = end.AddDays(1);
                end = pEnd;
            }


            return approvedHours;
        }

        public virtual decimal? GetApprovedHoursForPeriod(SupportConceptCostBearerRelDC relDC,
                                                          SupportConceptApprovalPeriodDC scap,
                                                          List<CostRatePeriodDC> costRatePeriods,
                                                          DateTime? pStart,
                                                          DateTime? pEnd,
                                                          bool useIsCalculationWithFactor)
        {
            decimal? approvedHours = GetApprovedHoursForPeriod(scap, costRatePeriods, pStart, pEnd);
            if (approvedHours.HasValue && useIsCalculationWithFactor)
                approvedHours = ApplyIsCalculationWithFactor(relDC, approvedHours);
            return approvedHours;

        }

        public virtual decimal? GetApprovedHoursForPeriod(SupportConceptApprovalPeriodDC scap,
                                                          List<CostRatePeriodDC> costRatePeriods,
                                                          DateTime? pStart,
                                                          DateTime? pEnd)
        {
            if (!pStart.HasValue || !pEnd.HasValue)
            {
                return null;
            }
	        if (scap.ApprovedBEInterval.HasValue && scap.ApprovedBEPerInterval.HasValue)
	        {
		        if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Monthly)
		        {
			        var months = GetTotalMonths(pStart.Value, pEnd.Value);
			        return months*scap.ApprovedBEPerInterval.Value;
		        }
	        }

            var periodInDays = (pEnd.Value.Date - pStart.Value.Date).Days + 1;

            return this.GetApprovedHoursPerDay(scap, costRatePeriods) * periodInDays;
        }

		public virtual decimal GetTotalMonths(DateTime start, DateTime end)
		{
			
			int restTageStart = 0;
			int restTageEnd = 0;

			DateTime startDt = start;
			DateTime endDt = end;

			if (startDt.Day > 1)
			{
				var endMonat = new DateTime(startDt.Year, startDt.Month, 1).AddMonths(1).AddDays(-1);

				if (endDt < endMonat)
				{
					endMonat = endDt;
				}
				restTageStart = endMonat.Subtract(startDt).Days + 1;
				startDt = endMonat.AddDays(1);
			}
			if (endDt.AddDays(1).Day != 1)
			{
				var startMonat = new DateTime(endDt.Year, endDt.Month, 1);

				restTageEnd += endDt.Subtract(startMonat).Days + 1;

				endDt = startMonat;
			}

			int fullMonths = 0;
			while (startDt < endDt)
			{
				fullMonths++;
				startDt = startDt.AddMonths(1);
			}

			decimal monthsTotal = fullMonths;


			if (restTageStart != 0)
			{
				monthsTotal += restTageStart / (decimal)DateTime.DaysInMonth(start.Date.Year, start.Date.Month);

			}
			if (restTageEnd != 0)
			{
				monthsTotal += restTageEnd / (decimal)DateTime.DaysInMonth(end.Date.Year, end.Date.Month);

			}
			return monthsTotal;
		}

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

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

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

                return serviceUnit.GetBEInMinutes(approvedBEPerDay) / 60m;
            }
            else if (scap.ApprovedBEPerInterval.HasValue)
            {
	            decimal approvedBEPerDay = 0;
	            if (scap.ApprovedBEInterval.HasValue)
	            {
		            if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Yearly)
		            {
						var totalDays = (scap.EndDate.Value - scap.StartDate.Value).Days + 1;

			            int totalYears = 0;
			            DateTime start = scap.StartDate.Value.AddYears(1);

						//Anzahl Jahre ausrechnen. Nur wenn das Enddatum genau auf das Datum eines Jahres fällt
			            while (start <= scap.EndDate.Value.AddDays(1))
			            {
				            totalYears++;

							start = start.AddYears(1);
				            if (start > scap.EndDate.Value.AddDays(1) && start != scap.EndDate.Value.AddDays(1).AddYears(1))
					            totalYears = 0;

			            }

			            if (totalYears > 0)
			            {
				            approvedBEPerDay = (scap.ApprovedBEPerInterval.Value*totalYears)/totalDays;

							return serviceUnit.GetBEInMinutes(approvedBEPerDay) / 60m;
			            }
		            }
		            
			        approvedBEPerDay = this.ConvertPerIntervall2PerDay(scap.ApprovedBEPerInterval.Value,
				            scap.ApprovedBEInterval.Value);
	            }
	            return serviceUnit.GetBEInMinutes(approvedBEPerDay) / 60m;
            }

            return null;
        }

        public virtual decimal? GetApprovedHoursPerDay(SupportConceptCostBearerRelDC relDC, DateTime date)
        {
            if (!relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }

            var scap = relDC.ApprovalPeriodList.FirstOrDefault(i => date.InBetween(i.Span, true));
            return scap != null
                       ? this.GetApprovedHoursPerDay(scap, relDC.CostBearer.CostRatePeriods)
                       : null;
        }

        public virtual decimal? GetApprovedHoursPerWeek(SupportConceptApprovalPeriodDC scap,
                                                        List<CostRatePeriodDC> costRatePeriods)
        {
            if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly)
            {
                var serviceUnit = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now);
                if (serviceUnit != null)
                    return serviceUnit.GetBEInHours(scap.ApprovedBEPerInterval);
            }
            return this.GetApprovedHoursPerDay(scap, costRatePeriods) * 7;
        }

        public virtual decimal? GetApprovedHoursPerWeek(SupportConceptApprovalPeriodDC scap,
                                                        List<CostRatePeriodDC> costRatePeriods, bool useIsCalculationWithFactor)
        {
            if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly)
            {
                var serviceUnit = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now);
                if (serviceUnit != null)
                    return serviceUnit.GetBEInHours(scap.ApprovedBEPerInterval);
            }
            return this.GetApprovedHoursPerDay(scap, costRatePeriods) * 7;
        }

        public virtual decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC, SupportConceptApprovalPeriodDC scap,
                                                        List<CostRatePeriodDC> costRatePeriods, bool useIsCalculationWithFactor)
        {
            decimal? hoursPerWeek = GetApprovedHoursPerWeek(scap, costRatePeriods);
            if (hoursPerWeek.HasValue && useIsCalculationWithFactor)
                hoursPerWeek = ApplyIsCalculationWithFactor(relDC, hoursPerWeek);
            return hoursPerWeek;
        }

        public virtual decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC,
                                                        DateTime date)
        {
            return GetApprovedHoursPerWeek(relDC, date, true);
        }


        public virtual decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC,
                                                        DateTime date, bool useIsCalculationWithFactor)
        {
            if (!relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }

            var scap = relDC.ApprovalPeriodList.FirstOrDefault(i => date.InBetween(i.Span, true));
            decimal? hoursPerWeek = null;
            if (scap != null)
            {
                hoursPerWeek = this.GetApprovedHoursPerWeek(scap, relDC.CostBearer.CostRatePeriods);
                if (useIsCalculationWithFactor)
                    hoursPerWeek = ApplyIsCalculationWithFactor(relDC, hoursPerWeek);
            }
            return hoursPerWeek;
        }

        public virtual decimal? ApplyIsCalculationWithFactor(SupportConceptCostBearerRelDC relDC, decimal? valueToApply)
        {
            if (valueToApply.HasValue && relDC.CostBearer != null && relDC.CostBearer.IsCalculatingWithFactor)
            {
                if (relDC.CostBearer.CostRatePeriods != null)
                {
                    decimal? rateFactor =
                        relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor);
                    if (rateFactor.HasValue && rateFactor.Value > 0)
                        valueToApply = (valueToApply.Value * 100) / (rateFactor.Value + 100);
                }

            }
            return valueToApply;
        }

        public virtual decimal? GetApprovedHoursPerWeekAverage(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor)
        {
            var duration = relDC.GetApprovedDuration();
            if (!duration.HasValue)
            {
                return null;
            }
            if (relDC.ApprovalPeriodList != null && relDC.ApprovalPeriodList.Count == 1)
            {
                decimal? hoursPerWeek = GetApprovedHoursPerWeek(relDC.ApprovalPeriodList[0], relDC.CostBearer.CostRatePeriods);
                if (useIsCalculationWithFactor)
                    hoursPerWeek = ApplyIsCalculationWithFactor(relDC, hoursPerWeek);
                return hoursPerWeek;
            }

            var averagePerDay = this.GetApprovedHoursTotal(relDC, useIsCalculationWithFactor) / duration.Value.Days;
            return averagePerDay * 7;
        }

        public virtual decimal? GetApprovedHoursTotal(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor)
        {
            if (relDC.ApprovalPeriodList == null || !relDC.GetApprovedDuration().HasValue)
            {
                return null;
            }
            decimal? hoursTotal = relDC.ApprovalPeriodList.Sum(i => this.GetApprovedHoursTotal(i, relDC.CostBearer.CostRatePeriods));
            if (useIsCalculationWithFactor)
                hoursTotal = ApplyIsCalculationWithFactor(relDC, hoursTotal);

            if (hoursTotal.HasValue)
                hoursTotal = Math.Round(hoursTotal.Value, 10, MidpointRounding.AwayFromZero);
            return hoursTotal;
        }

        public virtual decimal? GetApprovedHoursTotal(SupportConceptApprovalPeriodDC scap,
                                                      List<CostRatePeriodDC> costRatePeriods)
        {
            var test = this.GetApprovedHoursForPeriod(scap, costRatePeriods, scap.StartDate, scap.EndDate);
            return test;
        }

        public virtual BillingData GetBillingData(ServiceRecordDC record)
        {
            BillingData data = new BillingData();
            data.BillingPeriodStart = record.Start;
            data.BillingPeriodEnd = record.End;

            CostRatePeriodDC hourlyRate = null;
            CostRatePeriodDC flatRate = null;

            bool useRatefactor = true;
            if (record.ServiceDescription.Category.IsBillable)
            {
                var crpList = record.ServiceDescription.CostRatePeriods;
                var ai = record.ServiceDescription.AccountingInterval;

                if (crpList.Count(c => c.CostRateValue.HasValue) == 0)
                {
                    crpList = record.ServiceDescription.Category.CostRatePeriods;
                    ai = record.ServiceDescription.Category.AccountingInterval;
                }
                if (crpList.Count(c => c.CostRateValue.HasValue) > 0)
                {
                    CostRatePeriodDC crp = crpList.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney,
                                                                            record.Start.Value);

                    if (ai.HasValue)
                    {
                        if (ai.Value == AccountingIntervalType.Hourly)
                            hourlyRate = crp;
                        else if (ai.Value == AccountingIntervalType.FlatRate)
                            flatRate = crp;
                    }
                }
            }

            if (hourlyRate != null || flatRate != null)
                useRatefactor = false;

            if (flatRate != null)
            {
	            if (record.DistanceInMeter.HasValue && record.DistanceInMeter.Value > 0)
	            {
		            data.UnitCount = record.DistanceInMeter.Value;
	            }
	            else
	            {
		            data.UnitCount = 1;
	            }
				data.AmountPerUnit = flatRate.CostRateValue;
				data.AmountTotal = data.UnitCount * data.AmountPerUnit;
                data.AccountingInterval = AccountingIntervalType.FlatRate;
            }
            else
            {
                if (hourlyRate == null)
                    hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record);

	            decimal rdMinutes = this.GetBillableDurationInMinutes(record);
				data.UnitCount = rdMinutes / 60m;


				if (hourlyRate != null)
					data.AmountPerUnit = hourlyRate.CostRateValue;

				data.AmountTotal = data.AmountPerUnit * data.UnitCount;
				data.AccountingInterval = AccountingIntervalType.Hourly;
	            
            }

            if (useRatefactor)
            {
                var rateFactor =
                    record.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor,
                                                                               record.Start.Value);
                if (rateFactor != null && rateFactor.CostRateValue.HasValue)
                {
                    data.RateFactor = rateFactor.CostRateValue;
                    data.GrossAmountTotal = data.AmountTotal + (data.AmountTotal * rateFactor.CostRateValue / 100);
                }
            }
            if (!data.GrossAmountTotal.HasValue)
                data.GrossAmountTotal = data.AmountTotal;


            return data;
        }


        public virtual decimal? GetBillableAmount(ServiceRecordDC record)
        {
            var rdMinutes = this.GetBillableDurationInMinutes(record);
            var hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record);
            decimal amount = 0;
            if (hourlyRate != null && hourlyRate.CostRateValue.HasValue)
                amount = (rdMinutes / 60m) * hourlyRate.CostRateValue.Value;

            var rateFactor = record.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, record.Start.Value);
            if (rateFactor != null && rateFactor.CostRateValue.HasValue)
            {
                return amount + (amount * rateFactor.CostRateValue / 100);
            }

            return amount;
        }

        public virtual decimal GetBillableDurationInMinutes(ServiceRecordDC record)
        {
            if (!record.ServiceDescription.Category.IsBillable)
            {
                return 0;
            }
            decimal rdMinutes = record.RoundedDuration;
            if (record.ServiceDescription.Category.Percentage != 100)
            {
                rdMinutes = (rdMinutes * record.ServiceDescription.Category.Percentage) / 100;
            }
            if (record.GroupEmployeeCount.HasValue)
                rdMinutes /= record.GroupEmployeeCount.Value;

            return rdMinutes;
        }

        public virtual decimal GetBillableDurationInMinutes(List<ServiceRecordDC> records)
        {
            return records.Sum(i => GetBillableDurationInMinutes(i));
        }

        public virtual decimal GetDurationInMinutesForBillableAmount(ServiceRecordDC record, decimal amount)
        {
            var hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record);
            var rateFactor = record.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, record.Start.Value);
            if (rateFactor != null && rateFactor.CostRateValue.HasValue)
            {
                amount = (amount / (100 + rateFactor.CostRateValue.Value)) * 100;
            }
            if (hourlyRate != null && hourlyRate.CostRateValue.HasValue)
                return (amount / hourlyRate.CostRateValue.Value) * 60m;
            return 0;
        }

        public virtual CostRatePeriodDC GetHourlyRatePeriodForServiceRecord(ServiceRecordDC record)
        {
            var qualificationOIDs = record.Employee.ValueListEntries
                .Where(i => i.Value == ValueListEntryType.StaffQualificationsType)
                .Select(i => i.Key)
                .ToList();

            var qualificationHourlyRates = record.CostBearer.CostRatePeriods.Where(i =>
                                                                                   i.CostRateType == CostRatePeriodType.HourlyRate &&
                                                                                   i.ValueListEntry != null &&
                                                                                   i.ValueListEntry.Type == ValueListEntryType.StaffQualificationsType);

            var matchingQualifications = qualificationHourlyRates
                .Where(i => qualificationOIDs.Contains(i.ValueListEntry.ValueListEntryOid.Value))
                .ToList();

            var applyingQualificationRates = matchingQualifications
                .GetCostRatePeriodForDateGroupedByValueListEntryOid(CostRatePeriodType.HourlyRate, record.Start.Value);

            if (applyingQualificationRates.Count > 0)
            {
                var maxAmount = applyingQualificationRates.Max(i => i.Value.CostRateValue);
                return applyingQualificationRates.First(i => i.Value.CostRateValue.Equals(maxAmount)).Value;
            }

            return record.CostBearer.CostRatePeriods
                .GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, record.Start.Value);
        }

        public virtual decimal GetHoursNotBillableDueToAbsenceTimes(SupportConceptCostBearerRelDC sc2cb,
                                                                    List<ServiceRecordDC> records,
                                                                    Dictionary<DateTimeSpan, decimal> absenceTimes,
                                                                    bool doRemoveFromRecordsList,
                                                                    bool doCrop)
        {
            return sc2cb.ApprovalPeriodList.Sum(i => this.GetHoursNotBillableDueToAbsenceTimes(i,
                                                                                               sc2cb.CostBearer.CostRatePeriods,
                                                                                               records,
                                                                                               absenceTimes,
                                                                                               doRemoveFromRecordsList,
                                                                                               doCrop));
        }

        public virtual decimal GetHoursNotBillableDueToAbsenceTimes(SupportConceptApprovalPeriodDC scap,
                                                                    List<CostRatePeriodDC> costRates,
                                                                    List<ServiceRecordDC> records,
                                                                    Dictionary<DateTimeSpan, decimal> absenceTimes,
                                                                    bool doRemoveFromRecordsList,
                                                                    bool doCrop)
        {
            if (scap.ApprovedFixedAmount.HasValue)
            {
                return 0m;
            }

            var hoursNotBillableAbsence = 0m;
            foreach (var iRecord in records.ToList())
            {
                if (!iRecord.Start.Value.InBetween(scap.Span, true))
                {
                    if (doRemoveFromRecordsList)
                    {
                        records.Remove(iRecord);
                    }

                    continue;
                }

                var absenceSpan = absenceTimes.FirstOrDefault(i => iRecord.Start.Value.InBetween(i.Key, true)).Key;

                if (absenceSpan != null)
                {
                    var rd = this.GetBillableDurationInMinutes(iRecord);

                    if (absenceTimes[absenceSpan] == 0)
                    {
                        if (doRemoveFromRecordsList)
                        {
                            records.Remove(iRecord);
                        }

                        hoursNotBillableAbsence += rd / 60m;
                        continue;
                    }

                    if (absenceTimes[absenceSpan] - rd >= 0)
                    {
                        absenceTimes[absenceSpan] -= rd;
                        continue;
                    }

                    hoursNotBillableAbsence += (rd - absenceTimes[absenceSpan]) / 60;

                    if (!doCrop)
                    {
                        if (doRemoveFromRecordsList)
                        {
                            records.Remove(iRecord);
                        }

                        continue;
                    }

                    hoursNotBillableAbsence += (rd - absenceTimes[absenceSpan]) / 60;
                    iRecord.End = iRecord.Start.Value.AddMinutes(Convert.ToDouble(absenceTimes[absenceSpan]));

                    var minuteInterval = costRates.GetCostRatePeriodForDate(CostRatePeriodType.MinutesIntervall, iRecord.Start.Value);
                    var duration = Convert.ToDecimal((iRecord.End - iRecord.Start).Value.TotalMinutes);

					//###CB 5min
                    if (minuteInterval != null && minuteInterval.CostRateValue.HasValue && minuteInterval.CostRateValue.Value > 0)
                    {
	                    iRecord.RoundedDuration = GetRoundedDuration((int)minuteInterval.CostRateValue.Value, duration);
                    }

                    absenceTimes[absenceSpan] = 0;
                }
            }

            return hoursNotBillableAbsence;
        }

        public virtual decimal GetHoursNotBillableDueToMoreThanApproved(SupportConceptCostBearerRelDC sc2cb,
                                                                        List<ServiceRecordDC> records,
                                                                        bool doRemoveFromRecordsList,
                                                                        bool doCrop)
        {
            return sc2cb.ApprovalPeriodList.Sum(i => this.GetHoursNotBillableDueToMoreThanApproved(i,
                                                                                                   sc2cb.CostBearer.CostRatePeriods,
                                                                                                   records,
                                                                                                   doRemoveFromRecordsList,
                                                                                                   doCrop));
        }

        public virtual decimal GetHoursNotBillableDueToMoreThanApproved(SupportConceptApprovalPeriodDC scap,
                                                                        List<CostRatePeriodDC> costRates,
                                                                        List<ServiceRecordDC> records,
                                                                        bool doRemoveFromList,
                                                                        bool doCrop)
        {
            if (scap.ApprovedFixedAmount.HasValue)
            {
                return 0m;
            }

            var approvedAmountCounter = this.GetApprovedAmountDefaultHourlyRate(scap, costRates) ?? 0m;
            var hoursNotBillableNotApproved = 0m;

            foreach (var iRecord in records.ToList())
            {
                if (!iRecord.Start.Value.InBetween(scap.Span, true))
                {
                    if (doRemoveFromList)
                    {
                        records.Remove(iRecord);
                    }

                    continue;
                }

                var amount = this.GetBillableAmount(iRecord) ?? 0m;
                if (approvedAmountCounter - amount >= 0)
                {
                    approvedAmountCounter -= amount;
                    continue;
                }

                if (approvedAmountCounter == 0 || !doCrop)
                {
                    if (doRemoveFromList)
                    {
                        records.Remove(iRecord);
                    }

                    hoursNotBillableNotApproved += this.GetBillableDurationInMinutes(iRecord) / 60m;
                    continue;
                }

                var oldDuration = this.GetBillableDurationInMinutes(iRecord);

                iRecord.RoundedDuration = this.GetDurationInMinutesForBillableAmount(iRecord, approvedAmountCounter);
                iRecord.End = iRecord.Start.Value.AddMinutes(Convert.ToInt32(iRecord.RoundedDuration));
                hoursNotBillableNotApproved += (oldDuration - iRecord.RoundedDuration) / 60m;
                approvedAmountCounter = 0;
            }

            return hoursNotBillableNotApproved;
        }


        public virtual decimal GetHoursNotBillableDueToOutOfSpan(SupportConceptCostBearerRelDC relDC, List<ServiceRecordDC> records)
        {
            decimal hoursOutOfSpan = 0m;

            DateTimeSpan span = new DateTimeSpan();
            span.StartDate = relDC.StartDate ?? DateTime.MinValue;
            span.EndDate = relDC.EndDate ?? DateTime.MaxValue;

            foreach (var iRecord in records)
            {

                if (!iRecord.Start.Value.InBetween(span, true))
                {
                    hoursOutOfSpan += this.GetBillableDurationInMinutes(iRecord) / 60m;
                }
            }

            return hoursOutOfSpan;
        }

        public virtual decimal GetMonthCount(DateTime startDate, DateTime endDate)
        {
            decimal count = 0;

            startDate = startDate.AddMonths(1);
            while (startDate < endDate.AddDays(1))
            {
                count++;
                startDate = startDate.AddMonths(1);
            }

            decimal restDays = endDate.Day;

            decimal days = DateTime.DaysInMonth(endDate.Year, endDate.Month);

            count += restDays / days;
            return count;
        }

        public virtual decimal? GetApprovedHoursTillNow(SupportConceptCostBearerRelDC relDC, DateTime? appointedDate)
        {

            decimal? totalApproved = null;


            if (relDC.ApprovalPeriodList != null)
            {
                if (relDC.ApprovalPeriodList.Count == 1)
                {
                    decimal? flsTotal = GetApprovedHoursTotal(relDC, true);
                    decimal durationInWeeks = GetDurationInWeeks(relDC) ?? 0;
                    decimal durationInWeeksTillNow = GetDurationInWeeksTillNow(relDC, appointedDate) ?? 0;

                    if (flsTotal.HasValue && durationInWeeks != 0)
                    {
                        if (Math.Round(durationInWeeks, 10) == Math.Round(durationInWeeksTillNow, 10))
                            return flsTotal.Value;
                        totalApproved = flsTotal.Value * ((durationInWeeksTillNow + (1m / 7m)) / durationInWeeks);
                    }
                    else
                    {
                        var approvedPerWeek = GetApprovedHoursPerWeekAverage(relDC, true) ?? 0m;

                        totalApproved = approvedPerWeek * (durationInWeeksTillNow + (1m / 7m));
                    }
                }
                else if (relDC.ApprovalPeriodList.Count > 1)
                {
                    DateTime calcDate = (appointedDate.HasValue && appointedDate.Value < DateTime.Now.Date)
                                                 ? appointedDate.Value
                                                 : DateTime.Now.Date;

                    totalApproved = 0;
                    foreach (var period in relDC.ApprovalPeriodList)
                    {
                        if (period.StartDate.HasValue && period.EndDate.HasValue && calcDate >= period.StartDate)
                        {
                            decimal days;
                            var approvedHoursPerDay =
                                    GetApprovedHoursPerDay(period, relDC.CostBearer.CostRatePeriods) ?? 0;

                            if (relDC.CostBearer.IsCalculatingWithFactor)
                            {
                                decimal? rateFactor =
                                    relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor);
                                if (rateFactor.HasValue && rateFactor.Value > 0)
                                    approvedHoursPerDay = (approvedHoursPerDay * 100) / (rateFactor.Value + 100);
                            }

                            if (calcDate >= period.EndDate.Value)
                            {
                                days = Convert.ToDecimal((period.EndDate.Value - period.StartDate.Value).Days + 1);
                            }
                            else
                            {
                                days = Convert.ToDecimal((calcDate - period.StartDate.Value).Days + 1);
                            }

                            totalApproved += (days * approvedHoursPerDay);
                        }
                    }
                }
            }

            return totalApproved;
        }

		public virtual decimal? GetApprovedHoursTillNow(SupportConceptApprovalPeriodDC scap, List<CostRatePeriodDC> costRates, DateTime? appointedDate, bool excludeRateFactor)
		{

			decimal? totalApproved = null;

			DateTime calcDate = (appointedDate.HasValue && appointedDate.Value < DateTime.Now.Date)
												 ? appointedDate.Value
												 : DateTime.Now.Date;


			if (scap.StartDate.HasValue && scap.EndDate.HasValue && calcDate >= scap.StartDate)
			{
				decimal days;
				var approvedHoursPerDay =
						GetApprovedHoursPerDay(scap, costRates) ?? 0;

				if (excludeRateFactor)
				{
					decimal? rateFactor = costRates.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor);
					if (rateFactor.HasValue && rateFactor.Value > 0)
						approvedHoursPerDay = (approvedHoursPerDay * 100) / (rateFactor.Value + 100);
				}

				if (calcDate >= scap.EndDate.Value)
				{
					days = Convert.ToDecimal((scap.EndDate.Value - scap.StartDate.Value).Days + 1);
				}
				else
				{
					days = Convert.ToDecimal((calcDate - scap.StartDate.Value).Days + 1);
				}

				totalApproved = (days * approvedHoursPerDay);
			}
					

			return totalApproved;
		}
        #endregion



        public virtual decimal? GetDurationInWeeks(SupportConceptCostBearerRelDC relDC)
        {
            if (relDC.StartDate != null && relDC.EndDate != null)
            {
                return Convert.ToDecimal((relDC.EndDate.Value - relDC.StartDate.Value).Days + 1) / 7;
            }
            return null;
        }

		
        public virtual decimal? GetDurationInWeeksTillNow(SupportConceptCostBearerRelDC relDC, DateTime? appointedDate)
        {

            if (relDC.StartDate != null && relDC.EndDate != null)
            {
                DateTime lCalcDate = (appointedDate.HasValue && appointedDate.Value < DateTime.Now.Date)
                                                 ? appointedDate.Value
                                                 : DateTime.Now.Date;

                lCalcDate = lCalcDate > relDC.EndDate
                                    ? relDC.EndDate.Value.Date.AddDays(1)
                                    : lCalcDate;
                return Convert.ToDecimal((lCalcDate - relDC.StartDate.Value).Days) / 7m;
            }

            return null;
        }

        public virtual IList<BillingData> GetFixedBillingData(SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, DateTime? start, DateTime? end)
        {
            IList<BillingData> billingDatas = new List<BillingData>();
            DateTime? dtStart = start ?? supportConceptApprovalPeriod.StartDate;
            DateTime? dtEnd = end ?? supportConceptApprovalPeriod.EndDate;

            if (dtStart.HasValue && dtEnd.HasValue)
            {
				decimal amountPerUnit;
				decimal unitCount;

				decimal? approvalPeriodFixedAmount = GetApprovedFixedAmountForPeriod(supportConceptApprovalPeriod, dtStart, dtEnd, out amountPerUnit, out unitCount);
                if (approvalPeriodFixedAmount.HasValue)
                {
                    billingDatas.Add(new BillingData()
                        {
                            AmountTotal = approvalPeriodFixedAmount,
                            GrossAmountTotal = approvalPeriodFixedAmount,
                            BillingPeriodStart = dtStart,
                            BillingPeriodEnd = dtEnd,
                            AccountingInterval = AccountingIntervalType.FlatRate,
							AmountPerUnit = amountPerUnit,
							UnitCount = unitCount

                        });
                }
                else if (supportConceptApprovalPeriod.ServiceCategory != null)
                {
                    var crpList = supportConceptApprovalPeriod.ServiceCategory.CostRatePeriods;
                    var ai = supportConceptApprovalPeriod.ServiceCategory.AccountingInterval;
                    if (ai.HasValue)
                    {
                        DateTime intervalStart = dtStart.Value;
                        DateTime intervalEnd = dtStart.Value;
                        while (intervalStart < dtEnd)
                        {
                            
                            switch (ai.Value)
                            {
                                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;
                                case AccountingIntervalType.FlatRate:
                                    intervalEnd = dtEnd.Value;
                                    break;
								default:
									intervalEnd = dtEnd.Value;
		                            break;

                            }

                            CostRatePeriodDC crp = crpList.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney,
                                                                                    intervalStart);
                            if (crp != null && crp.CostRateValue.HasValue)
                            {
                                billingDatas.Add(new BillingData()
                                {
                                    AmountTotal = crp.CostRateValue,
                                    GrossAmountTotal = crp.CostRateValue,
                                    BillingPeriodStart = intervalStart,
                                    BillingPeriodEnd = intervalEnd.AddDays(-1),
                                    AccountingInterval = ai

                                });
                            }

                            intervalStart = intervalEnd;
                        }
                    }
                }
            }

            
            return billingDatas;
        }

        public virtual List<AccountingPeriod> GetAccountingPeriods(SupportConceptCostBearerRelDC relDC, List<ServiceRecordDC> recordDCS, DateTimeSpan span)
        {
			Dictionary<long, bool> groupBookingDict = new Dictionary<long, bool>();
            List<AccountingPeriod> apList = new List<AccountingPeriod>();
            Dictionary<AccountingPeriod, AccountingPeriod> ap2apDict  = new Dictionary<AccountingPeriod, AccountingPeriod>();

			foreach (var sr in recordDCS)
			{
				if (sr.ServiceDescription.Category.IsBillable && sr.Start.HasValue)
				{
					if (sr.GroupOid == null || !groupBookingDict.ContainsKey(sr.GroupOid.Value))
					{
						if (sr.Start.Value >= new DateTime(2015, 1, 1))
						{
							int test = 0;
						}
						AccountingPeriod ap = GetAccountingPeriodForServiceRecord(relDC, sr, span);
						
						if (ap != null)
						{
							if (ap2apDict.ContainsKey(ap))
							{
								ap2apDict[ap].ServiceRecordsInPeriod.Add(sr);
							}
							else
							{
								ap2apDict.Add(ap, ap);
								apList.Add(ap);
							}
						}
						if (sr.GroupOid != null && !groupBookingDict.ContainsKey(sr.GroupOid.Value))
						{
							groupBookingDict.Add(sr.GroupOid.Value, true);
						}
					}
				}
			}
	        return apList;
        }

        public virtual AccountingPeriod GetAccountingPeriodForServiceRecord(SupportConceptCostBearerRelDC relDC, ServiceRecordDC sr, DateTimeSpan span)
        {
            CostRatePeriodDC crp = null;
            CostRatePeriodDC previousCrp = null;
            AccountingIntervalType intervalType = AccountingIntervalType.Hourly;
            ServiceCategoryDC category = null;
            
            var scap = FindApprovalPeriodForDateAndCategory(relDC, sr.Start.Value,
                                                                sr.ServiceDescription.Category);
                
            if (scap != null)
            {
                category = scap.ServiceCategory;

                crp =
                sr.ServiceDescription.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney,
                                                                                sr.Start.Value);
                if (crp != null)
                {
                    previousCrp = GetPreviousCrp(crp, sr.ServiceDescription.CostRatePeriods);
                    intervalType = sr.ServiceDescription.AccountingInterval ?? AccountingIntervalType.Hourly;
                    category = sr.ServiceDescription.Category;
                }
                else
                {
                    crp =
                    sr.ServiceDescription.Category.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney,
                                                                                    sr.Start.Value);
                    if (crp != null)
                    {
                        previousCrp = GetPreviousCrp(crp, sr.ServiceDescription.Category.CostRatePeriods);
                        intervalType = sr.ServiceDescription.Category.AccountingInterval ??
                                        AccountingIntervalType.Hourly;
                        category = sr.ServiceDescription.Category;
                    }
                }

                if (crp == null)
                {
                    if (scap.ApprovedFixedAmount.HasValue && scap.ApprovedFixedAmountInterval.HasValue)
                    {
                        crp = new CostRatePeriodDC();
                        crp.CostRateValue = scap.ApprovedFixedAmount.Value;
                        crp.CostRateType = CostRatePeriodType.AmountOfMoney;

						intervalType = ConvertApprovalToAccountingIntervalType(scap.ApprovedFixedAmountInterval.Value);
                    }
                }

                CostRatePeriodDC rateFactor = null;
                CostRatePeriodDC unit = null;
                if (crp == null)
                {
                    crp = relDC.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate,
                                                                                    sr.Start.Value);
                    rateFactor = relDC.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor,
                                                                                    sr.Start.Value);

                    unit = relDC.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit,
                                                                                    sr.Start.Value);

                    previousCrp = GetPreviousCrp(crp, relDC.CostBearer.CostRatePeriods);
                }

                if (crp != null)
                {
                    AccountingPeriod ap = new AccountingPeriod();
                    ap.PeriodStart = scap.StartDate ?? DateTime.MinValue;
                    ap.PeriodEnd = scap.EndDate ?? DateTime.MaxValue;

                    if (span != null)
                    {
                        if (span.StartDate > ap.PeriodStart)
                            ap.PeriodStart = span.StartDate;
                        if (span.EndDate < ap.PeriodEnd)
                            ap.PeriodEnd = span.EndDate;
                    }
                        
                    ap.Amount = crp.CostRateValue ?? 0;
                    ap.AccountingInterval = intervalType;
                    ap.ServiceCategory = category;

                    ap.ServiceRecordsInPeriod.Add(sr);

                    ap.CostRatesInPeriod = new List<CostRatePeriodDC>();
                    ap.CostRatesInPeriod.Add(crp);

                    if (crp.EndDate.HasValue && crp.EndDate.Value.Date < ap.PeriodEnd.Date)
                        ap.PeriodEnd = crp.EndDate.Value;

                    if (previousCrp != null && previousCrp.EndDate.HasValue && previousCrp.EndDate.Value.Date >= ap.PeriodStart.Date)
                    {
                        ap.PeriodStart = previousCrp.EndDate.Value.Date.AddDays(1);
                    }
                    if (rateFactor != null)
                    {
                        ap.CostRatesInPeriod.Add(rateFactor);
                        ap.RateFactor = rateFactor.CostRateValue ?? 0;
                    }

                    if (unit != null)
                    {
                        ap.CostRatesInPeriod.Add(unit);
                        ap.UnitName = unit.UnitName;
                    }
                    CalculateApprovedAmounts(ap, scap);
                    return ap;
                }
            }
            
            return null;
        }

        private CostRatePeriodDC GetPreviousCrp(CostRatePeriodDC crp, List<CostRatePeriodDC> allCrpList)
        {
            var oldList = allCrpList.GetOldRates(crp.CostRateType).OrderBy(i => i.EndDate);
            
            if (!crp.EndDate.HasValue)
                return oldList.LastOrDefault(c => c.EndDate.HasValue);
            else
                return oldList.LastOrDefault(c => c.EndDate < crp.EndDate);
                
        }

        public virtual void CalculateApprovedAmounts(AccountingPeriod ap, SupportConceptApprovalPeriodDC scap)
        {
	        ap.SupportConceptApprovalPeriod = scap;

            if (scap.ApprovedBETotal.HasValue)
            {
                ap.ApprovedPerUnit = scap.ApprovedBETotal.Value;
                ap.ApprovalUnit = AccountingIntervalType.FlatRate;
            }
            else if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue)
            {
                ap.ApprovedPerUnit = scap.ApprovedBEPerInterval.Value;
                switch (scap.ApprovedBEInterval.Value)
                {
                    case SupportConceptApprovalInterval.Daily:
                        ap.ApprovalUnit = AccountingIntervalType.Daily;
                        break;
                    case SupportConceptApprovalInterval.Weekly:
                        ap.ApprovalUnit = AccountingIntervalType.Weekly;
                        break;
                    case SupportConceptApprovalInterval.Fortnightly:
                        ap.ApprovalUnit = AccountingIntervalType.Fortnightly;
                        break;
                    case SupportConceptApprovalInterval.Monthly:
                        ap.ApprovalUnit = AccountingIntervalType.Monthly;
                        break;
                    case SupportConceptApprovalInterval.Quarterly:
                        ap.ApprovalUnit = AccountingIntervalType.Quarterly;
                        break;
                    case SupportConceptApprovalInterval.HalfYearly:
                        ap.ApprovalUnit = AccountingIntervalType.HalfYearly;
                        break;
                    case SupportConceptApprovalInterval.Yearly:
                        ap.ApprovalUnit = AccountingIntervalType.Yearly;
                        break;
                }
            }
            //ap.MaxApprovedAmount = GetApprovedAmount(ap, scap);
            ap.MaxApprovedUnitCount = GetApprovedBETotalInPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd);

            //var test2 = GetApprovedAmountDefaultHourlyRate(scap, ap.CostRatesInPeriod);
            //var test3 = GetApprovedAmountDefaultHourlyRateForPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd);
            //var test4 = GetApprovedFixedAmount(scap);
            //var test5 = GetApprovedFixedAmountForPeriod(scap, ap.PeriodStart, ap.PeriodEnd);

            if (scap.ApprovedFixedAmount.HasValue)
            {
                ap.MaxApprovedAmount = scap.ApprovedFixedAmount.Value;
            }
        }

        public virtual decimal? GetApprovedAmount(AccountingPeriod ap, SupportConceptApprovalPeriodDC scap)
        {
	        decimal t1, t2;
				
            return scap.ApprovedFixedAmount.HasValue
                       ? this.GetApprovedFixedAmountForPeriod(scap, ap.PeriodStart, ap.PeriodEnd, out t1, out t2)
                       : this.GetApprovedAmountDefaultHourlyRateForPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd);
        }


        public virtual decimal? GetApprovedBETotalInPeriod(SupportConceptApprovalPeriodDC scap,
                                                   List<CostRatePeriodDC> costRatePeriods,
                                                   DateTime start, DateTime end)
        {
            return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursForPeriod(scap, costRatePeriods, start, end));
        }



		public virtual int GetRoundedDuration(DataContracts.Compact.CompactOrganisationDC org, decimal durationInMinutes)
		{
			int minuteInterval = org.ActualMinuteIntervall;
			return GetRoundedDuration(minuteInterval, durationInMinutes);
		}

		public virtual int GetRoundedDuration(int minuteInterval, decimal durationInMinutes)
		{
			decimal lRoundedDuration = durationInMinutes;

			if (minuteInterval > 0)
			{
				lRoundedDuration = durationInMinutes % minuteInterval != 0
										? durationInMinutes + (minuteInterval - (durationInMinutes % minuteInterval))
										: durationInMinutes;
			}

			return (int)Math.Round(lRoundedDuration, 0, MidpointRounding.AwayFromZero);
		}

		public static AccountingIntervalType ConvertApprovalToAccountingIntervalType(SupportConceptApprovalInterval supportConceptApprovalInterval)
		{
			switch (supportConceptApprovalInterval)
			{
				case SupportConceptApprovalInterval.Daily:
					return AccountingIntervalType.Daily;
				case SupportConceptApprovalInterval.Weekly:
					return AccountingIntervalType.Weekly;
				case SupportConceptApprovalInterval.Fortnightly:
					return AccountingIntervalType.Fortnightly;
				case SupportConceptApprovalInterval.Monthly:
					return AccountingIntervalType.Monthly;
				case SupportConceptApprovalInterval.Quarterly:
					return AccountingIntervalType.Quarterly;
				case SupportConceptApprovalInterval.HalfYearly:
					return AccountingIntervalType.HalfYearly;
				default:
					return AccountingIntervalType.Yearly;
			}
		}
	}
}