using System;
using BeWo.Report.ReportObjects;
using BeWo.Report;
using BeWo.Data.Entities;
using BeWo.Data.Access;
using System.Collections.Generic;
using BS.Shared.Core;
using BeWo.Service.ServiceImplementations;

namespace BeWo.WohnhelferRemscheidReports
{
    public partial class Mitarbeiterauswertung : DevExpress.XtraReports.UI.XtraReport, IBeWoReport<QueryRO>
    {
        private decimal maxWochenstunden = 39;

        

            //Zeile Gesamt mit "FLS Stunden Soll"
        public Mitarbeiterauswertung()
        {
            InitializeComponent();
        }

        public void SetReportDataSource(QueryRO pRO)
        {
            InitDataSource(pRO);
            this.bindingSource1.DataSource = pRO;
        }

        private void InitDataSource(QueryRO ro)
        {
            IList<Employee> employees = DAOFactory.GenericDAO.GetAllActiveAndArchived<Employee>();
            Dictionary<long, Employee> empDict = new Dictionary<long, Employee>();
            foreach (var item in employees)
            {
                empDict.Add(item.Oid.Value, item);
            }
            DateTime monat = DateTime.MinValue;

            decimal sollGesamt = 0;
            decimal relationGesamt = 0;
            decimal gesamtGesamt = 0;
            decimal flsSollGesamt = 0;
            decimal flsPlusMinusGesamt = 0;
            decimal flsIstGesamt = 0;


            foreach (BeWo.Report.ReportObjects.QueryRO.Row row in ro.Rows)
            {
                if (monat == DateTime.MinValue)
                {
                    monat = DateTime.Parse(row.Field2.ToString());
                }
                long oid = 0;
                if (Int64.TryParse(row.Field1.ToString(), out oid))
                {
                    if (empDict.ContainsKey(oid))
                    {
                        Employee emp = empDict[oid];

                        int arbeitstage = GetArbeitstageImMonat(row.Field2);

                        decimal bu = 0;

                        row.Field2 = emp.Person.LastName;
                        row.Field3 = null;

                        Contract c = emp.GetValidContractForDate(DateTime.Now);

                        bool istMitMehrstunden = false;
                        bool istHonorarkraft = false;

                        if (c != null)
                        {
                            if (c.WeeklyTotalHours.HasValue)
                            {
                                if (c.WeeklyTotalHours.HasValue)
                                {
                                    bu = c.WeeklyTotalHours.Value / maxWochenstunden;
                                }
                            }

                            EmploymentType et = c.EmploymentType;
                            if (et != null)
                            {
                                if (et.Oid.Value == 19)
                                    istMitMehrstunden = true;
                                else if (et.Oid.Value == 18)
                                    istHonorarkraft = true;
                            }
                            if (c.EntryDate > monat)
                                bu = 0;
                        } else
                        {
                            row.Field20 = "NoContract";
                        }

                        decimal flsIst = 0;
                        decimal mittelbarIst = 0;

                        DateTimeSpan span = new DateTimeSpan();
                        span.StartDate = monat;

                        span.EndDate = monat.Date.AddMonths(1);

                        //IList<ServiceRecord> records2 = DAOFactory.SearchDAO.FindEmployeeServiceRecords(emp.Oid.Value, span, null);

                        IList<ServiceRecord> records = DAOFactory.SearchDAO.FindServiceRecords(emp.Oid, null);
                        records = AnalysisServiceImp.FilterActiveServiceRecords(records);
                        foreach (var item in records)
                        {
                            //if (item.ServiceDescription != null && item.ServiceDescription.ServiceCategory != null && item.ServiceDescription.ServiceCategory.Name.ToLower().IndexOf("fehlzeit") >= 0)
                            //{
                            if (!IsServiceRecordFehlzeit(item))
                            {
                                if (item.Start.Value >= monat && item.Start.Value < monat.AddMonths(1))
                                {
                                    if (item.ServiceDescription != null &&
                                        item.ServiceDescription.ServiceCategory != null &&
                                        item.ServiceDescription.ServiceCategory.IsBillable)
                                        flsIst += item.RoundedDuration;
                                    else
                                        mittelbarIst += item.RoundedDuration;
                                }
                            }
                            //}
                        }
                        flsIst /= 60m;
                        mittelbarIst /= 60m;

                        row.Field6 = String.Format("{0:0.00}", flsIst);
                        row.Field7 = String.Format("{0:0.00}", mittelbarIst);
                        //Decimal.TryParse(row.Field6.ToString(), out flsIst);
                        //Decimal.TryParse(row.Field7.ToString(), out mittelbarIst);

                        row.Field3 = String.Format("{0:0.00}", bu);

                        decimal flsSollGesamtS4 = arbeitstage * (maxWochenstunden / 5) * bu;

                        row.Field4 = String.Format("{0:0.00}", flsSollGesamtS4);

                        IList<AbsenceTime> absenceTimes = emp.AbsenceTimes;





                        decimal? fehlZeitenS5 = GetFehlzeiten(records, monat);
                        row.Field5 = String.Format("{0:0.00}", fehlZeitenS5);

                        decimal? urlaubS6 = GetUrlaubKrankheit(absenceTimes, monat);

                        row.Field6 = String.Format("{0:0.00}", urlaubS6);
                        decimal tatsaechlicheSollStdS7 = flsSollGesamtS4;
                        if (fehlZeitenS5.HasValue)
                            tatsaechlicheSollStdS7 -= fehlZeitenS5.Value;
                        if (urlaubS6.HasValue)
                            tatsaechlicheSollStdS7 -= (urlaubS6.Value * (maxWochenstunden / 5) * bu);
                        row.Field7 = String.Format("{0:0.00}", tatsaechlicheSollStdS7);

                        decimal flsSollS8 = tatsaechlicheSollStdS7;
                        if (c != null && c.WeeklyFLS.HasValue && c.WeeklyTotalHours.HasValue && c.WeeklyTotalHours.Value != 0)
                            flsSollS8 = tatsaechlicheSollStdS7 * (c.WeeklyFLS.Value / c.WeeklyTotalHours.Value);

                        row.Field8 = String.Format("{0:0.00}", flsSollS8);
                        row.Field9 = String.Format("{0:0.00}", flsIst);
                        row.Field10 = String.Format("{0:0.00}", flsIst - flsSollS8);
                        row.Field11 = String.Format("{0:0.00}", flsIst + mittelbarIst);
                        if (flsIst + mittelbarIst > 0)
                        {
                            row.Field12 = String.Format("{0:0.00}%  ", (flsIst / (flsIst + mittelbarIst)) * 100);
                        //    if (flsSollS8 != 0)
                        //        row.Field12 += String.Format("{0:0.00}%", ((flsIst * (30m / 39m)) / flsSollS8) * 100);
                        //    else
                        //        row.Field12 += "";

                            
                        }

                        decimal stundengesamt = flsIst + mittelbarIst;
                        decimal? ueberstunden = stundengesamt - tatsaechlicheSollStdS7;
                        decimal? ueberstundenVormonat;
                        decimal? mehrstunden = null;
                        decimal? mehrstundenVormonat = null;


                        BerechneÜberstundenBis(emp, monat, bu, istMitMehrstunden, out ueberstundenVormonat, out mehrstundenVormonat);


                        if (istMitMehrstunden)
                        {
                            decimal maxMonat = arbeitstage * (maxWochenstunden / 5);
                            if (fehlZeitenS5.HasValue)
                                maxMonat -= fehlZeitenS5.Value;
                            if (urlaubS6.HasValue)
                                maxMonat -= (urlaubS6.Value * (maxWochenstunden / 5) * bu);
                            if (stundengesamt > maxMonat)
                            {
                                ueberstunden = stundengesamt - maxMonat;
                                mehrstunden = maxMonat - tatsaechlicheSollStdS7;
                            }
                            else
                            {
                                mehrstunden = ueberstunden;
                                ueberstunden = null;

                            }
                        }

                        row.Field13 = String.Format("{0:0.00}", ueberstunden);
                        row.Field14 = String.Format("{0:0.00}", ueberstundenVormonat);
                        row.Field15 = String.Format("{0:0.00}", ueberstundenVormonat + ueberstunden);

                        row.Field16 = String.Format("{0:0.00}", mehrstunden);
                        row.Field17 = String.Format("{0:0.00}", mehrstundenVormonat);
                        row.Field18 = String.Format("{0:0.00}", mehrstundenVormonat + mehrstunden);

                        if (istHonorarkraft)
                        {
                            row.Field3 = "";
                            row.Field4 = "";
                            row.Field5 = "";
                            row.Field6 = "";
                            row.Field7 = "";
                            row.Field8 = "";
                            //row.Field9 = "";
                            //row.Field10 = "";
                            //row.Field11 = "";
                            //row.Field12 = "";
                            row.Field13 = "";
                            row.Field14 = "";
                            row.Field15 = "";
                            row.Field16 = "";
                            row.Field17 = "";
                            row.Field18 = "";
                        }


                        sollGesamt += tatsaechlicheSollStdS7;
                        flsSollGesamt += flsSollS8;
                        flsIstGesamt += flsIst;
                        flsPlusMinusGesamt += (flsIst - flsSollS8);
                        gesamtGesamt += flsIst + mittelbarIst;
                        
                        
                        
                    }
                }

                
            }
            
            cellSollGesamt.Text = String.Format("{0:0.00}", sollGesamt); //7
            cellFLSSollGesamt.Text = String.Format("{0:0.00}", flsSollGesamt); //8
            cellFLSIstGesamt.Text = String.Format("{0:0.00}", flsIstGesamt); //9
            cellFLSPlusMinusGesamt.Text = String.Format("{0:0.00}", flsPlusMinusGesamt);//10
            cellGesamtGesamt.Text = String.Format("{0:0.00}", gesamtGesamt);//11


            if (flsSollGesamt != 0)
                cellRelationGesamt.Text += String.Format("{0:0.00}%", ((flsIstGesamt * (30m / 39m)) / flsSollGesamt) * 100m);
            else
                cellRelationGesamt.Text += "";


            lblMonat.Text = String.Format("{0:MMMM yy}", monat);

        }

        

        private void BerechneÜberstundenBis(Employee emp, DateTime monat, decimal bu, bool mehrstundenBerechnen, out decimal? ueberstunden, out decimal? mehrstunden)
        {
            ueberstunden = null;
            mehrstunden = null;

            IList<ServiceRecord> records = DAOFactory.SearchDAO.FindServiceRecords(emp.Oid, null);
            records = AnalysisServiceImp.FilterActiveServiceRecords(records);

            //decimal minutenGesamt = 0;
            //decimal soll = 0;

            Contract c = GetFirstContract(emp);

            //decimal stundenAbwesend = 0;

            if (c != null && c.EntryDate.HasValue)
            {
                DateTime start = c.EntryDate.Value;

                while (start < monat)
                {
                    decimal minutenMonat = 0;
                    DateTime ende = start.AddMonths(1);
                    ende = new DateTime(ende.Year, ende.Month, 1);

                    foreach (var item in records)
                    {
                        if (!IsServiceRecordFehlzeit(item))
                        {
                            if (item.Start.HasValue && item.Start >= start && item.Start < ende)
                            {
                                minutenMonat += item.RoundedDuration;
                            }
                        }
                    }

                    int arbeitstage = GetArbeitstageImBereich(start, ende);

                    decimal maxSollMonat = arbeitstage * (maxWochenstunden / 5);
                    decimal sollMitarbeiter = maxSollMonat * bu;

                    decimal? urlaub = GetUrlaubKrankheit(emp.AbsenceTimes, start);
                    decimal? fehlzeit = GetFehlzeiten(records, start);

                    if (fehlzeit.HasValue)
                    {
                        sollMitarbeiter -= fehlzeit.Value;
                        maxSollMonat -= fehlzeit.Value;
                    }
                    if (urlaub.HasValue)
                    {
                        sollMitarbeiter -= (urlaub.Value * (maxWochenstunden / 5) * bu);
                        maxSollMonat -= (urlaub.Value * (maxWochenstunden / 5) * bu);
                    }

                    decimal stundenMonat = minutenMonat /= 60m;



                    if (mehrstundenBerechnen)
                    {
                        if (!mehrstunden.HasValue)
                            mehrstunden = 0;

                        if (stundenMonat > maxSollMonat)
                        {
                            if (!ueberstunden.HasValue)
                                ueberstunden = 0;
                            ueberstunden += (stundenMonat - maxSollMonat);
                            mehrstunden += (maxSollMonat - sollMitarbeiter);
                        }
                        else
                        {
                            mehrstunden += (stundenMonat - sollMitarbeiter);
                        }
                    }
                    else
                    {
                        if (!ueberstunden.HasValue)
                            ueberstunden = 0;
                        ueberstunden += stundenMonat - sollMitarbeiter;
                    }

                    
                    start = ende;
                }
            }




        }

        private Contract GetFirstContract(Employee emp)
        {
            DateTime minDate = DateTime.MaxValue;
            Contract contractWithMinDate = null;
            if (emp.Contracts != null)
            {
                foreach (var item in emp.Contracts)
                {
                    if (!item.EntryDate.HasValue)
                    {
                        contractWithMinDate = item;
                        minDate = DateTime.MinValue;
                    }
                    else if (item.EntryDate.Value < minDate)
                    {
                        minDate = item.EntryDate.Value;
                        contractWithMinDate = item;
                    }
                }
            }
            return contractWithMinDate;
        }

        private decimal? GetUrlaubKrankheit(IList<AbsenceTime> absenceTimes, DateTime monat)
        {
            decimal? tage = null;
            if (absenceTimes != null)
            {
                foreach (var item in absenceTimes)
                {
                    //if (!String.IsNullOrEmpty(item.AbsenceReason.Description) && (item.AbsenceReason.Description.ToLower().IndexOf("urlaub") >= 0
                    //                                                              || item.AbsenceReason.Description.ToLower().IndexOf("krank") >= 0))
                    //{


                    DateTimeSpan span = new DateTimeSpan();

                    span.StartDate = monat;

                    if (item.Start.HasValue && item.Start.Value > span.StartDate)
                    {
                        span.StartDate = item.Start.Value;
                    }
                    span.EndDate = monat.AddMonths(1);
                    if (item.End.HasValue && item.End.Value.Date.AddDays(1) < span.EndDate)
                    {
                        span.EndDate = item.End.Value.Date.AddDays(1);
                    }
                    if (span.EndDate >= monat && span.StartDate < monat.AddMonths(1))
                    {
                        //if (item.AbsenceReason.)
                        if (!tage.HasValue)
                            tage = 0;

                        tage += (decimal)span.GetTimeSpan().TotalHours;
                    }
                    //}
                }
            }
            if (tage.HasValue)
                tage /= 24;
            return tage;
        }

        private decimal? GetFehlzeiten(IList<ServiceRecord> records, DateTime monat)
        {
            decimal? stunden = null;
            decimal minuten = 0;
            if (records != null)
            {
                foreach (var item in records)
                {
                    if (IsServiceRecordFehlzeit(item))
                    {
                        if (item.Start.Value >= monat && item.Start.Value < monat.AddMonths(1))
                            minuten += item.RoundedDuration;
                    }
                }
            }
            if (minuten != 0)
                stunden = minuten / 60;
            return stunden;
        }



        private int GetArbeitstageImMonat(object datum)
        {
            DateTime dt = DateTime.Now;
            if (DateTime.TryParse(datum.ToString(), out dt))
            {
                DateTime start = new DateTime(dt.Year, dt.Month, 1);

                DateTime end = start.AddMonths(1);

                return GetArbeitstageImBereich(start, end);
            }
            return 0;
        }
        private int GetArbeitstageImBereich(DateTime von, DateTime bis)
        {
            DateTime dt = DateTime.Now;

            DateTime start = von;
            DateTime end = bis;

            TimeSpan ts = end.Subtract(start);

            int maxDays = (int)ts.TotalDays;

            while (start < end)
            {
                if (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday)
                {
                    maxDays--;
                }
                else
                {
                    if (IstFeiertag(start))
                        maxDays--;
                }
                start = start.AddDays(1);
            }
            return maxDays;

        }

        private bool IstFeiertag(DateTime start)
        {
            List<DateTime> feiertage = GetFeiertage(start.Year);

            foreach (var dateTime in feiertage)
            {
                if (start == dateTime)
                    return true;
            }
            return false;
        }

        private List<DateTime> GetFeiertage(int year)
        {
            List<DateTime> list = new List<DateTime>();
            list.Add(new DateTime(year, 1, 1)); // "Neujahr"));
            //list.Add(new DateTime(year, 1, 6)); // , "Heilige Drei Könige"));
            list.Add(new DateTime(year, 5, 1)); // , "Tag der Arbeit"));
            //list.Add(new DateTime(year, 8, 15)); // , "Mariä Himmelfahrt"));
            list.Add(new DateTime(year, 10, 3)); // , "Tag der dt. Einheit"));
            //list.Add(new DateTime(year, 10, 31)); // , "Reformationstag"));
            list.Add(new DateTime(year, 11, 1)); // , "Allerheiligen "));
            list.Add(new DateTime(year, 12, 24)); // , "Heilig Abend"));
            list.Add(new DateTime(year, 12, 25)); // , "1. Weihnachtstag"));
            list.Add(new DateTime(year, 12, 26)); // , "2. Weihnachtstag"));
            DateTime osterSonntag = GetOsterSonntag(year);
            //list.Add(osterSonntag); // , "Ostersonntag"));
            //list.Add(osterSonntag.AddDays(-3)); // , "Gründonnerstag"));
            list.Add(osterSonntag.AddDays(-48)); // , "Rosenmonatg"));
            list.Add(osterSonntag.AddDays(-2)); // , "Karfreitag"));
            list.Add(osterSonntag.AddDays(1)); // , "Ostermontag"));
            list.Add(osterSonntag.AddDays(39)); // , "Christi Himmelfahrt"));
            //list.Add(osterSonntag.AddDays(49)); // , "Pfingstsonntag"));
            list.Add(osterSonntag.AddDays(50)); // , "Pfingstmontag"));
            list.Add(osterSonntag.AddDays(60)); // , "Fronleichnam"));

            return list;
        }

        private DateTime GetOsterSonntag(int year)
        {

            int g, h, c, j, l, i;

            g = year % 19;
            c = year / 100;
            h = ((c - (c / 4)) - (((8 * c) + 13) / 25) + (19 * g) + 15) % 30;
            i = h - (h / 28) * (1 - (29 / (h + 1)) * ((21 - g) / 11));
            j = (year + (year / 4) + i + 2 - c + (c / 4)) % 7;

            l = i - j;
            int month = (int)(3 + ((l + 40) / 44));
            int day = (int)(l + 28 - 31 * (month / 4));

            return new DateTime(year, month, day);

        }

        private bool IsServiceRecordFehlzeit(ServiceRecord sr)
        {
            if (sr.ServiceDescription != null && sr.ServiceDescription.ServiceCategory != null && sr.ServiceDescription.ServiceCategory.Name.ToLower().IndexOf("fehlzeit") >= 0)
                return true;
            return false;
        }
    }
}
