﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Office.Interop.Excel;
using WinformsAsync;

namespace VistaMedTools
{
    public partial class PlanExecutionFomsReport : ExcelReport<PlanExecutionFomsReportParams>
    {
        protected List<FomsReportRowData> reportData = new List<FomsReportRowData>();
        private FomsReportRowData totalsReportData;


        public PlanExecutionFomsReport(IBackgroundContext context, PlanExecutionFomsReportParams args, 
            string fileName = null) : base(context, args, fileName)
        {
        }

        public static void FillReport(IBackgroundContext context, PlanExecutionFomsReportParams args)
        {
            (new PlanExecutionFomsReport(context, args)).buildReportInternal(args);
        }
        
        protected override string getReportCaption()
        {
            return string.Format(@"ФОРМИРУЕМ ОТЧЕТ ДЛЯ ФОМС ПО ВЫПОЛНЕНИЮ ПЛАНА ЗАКАЗА-ЗАДАНИЯ");
        }

        protected override void fillReport(BuildReportContext execContext)
        {
            foreach (var smoData in smoDataList)
            {
                Action<FomsReportRowType, ReportRow> act = (x, y) =>
                {
                    var data = reportData.Where(z => z.SmoCode == smoData.SmoCode && z.RowType == x).ToList();
                    y.Fill(data);
                };

                act(FomsReportRowType.Plan, smoData.Plan);
                act(FomsReportRowType.Fact, smoData.Fact);

                if (totalsReportData != null)
                {
                    var ws = (Worksheet) Workbook.Worksheets[1];
                    var colIndex = 2;
                    ws.Cells.SetCellValue(5, colIndex++, totalsReportData.StacFullCount);
                    ws.Cells.SetCellValue(5, colIndex++, totalsReportData.StacDayCount + totalsReportData.StacApuCount);
                    ws.Cells.SetCellValue(5, colIndex++, totalsReportData.EmergencyCount);
                    ws.Cells.SetCellValue(5, colIndex++, totalsReportData.TreatmentCount);
                    ws.Cells.SetCellValue(5, colIndex++, totalsReportData.VisitCount + totalsReportData.ProfilacticCount);
                    ws.Cells.SetCellValue(5, colIndex++, totalsReportData.AmbulanceCount);
                }

                if (Args.HideFactCountRows)
                {
                    ((Range) smoData.Worksheet.Rows[smoData.Fact.RowIndex]).Hidden = true;
                }
            }
        }

        protected override bool selectDataForReport(BuildReportContext execContext)
        {
            var p = new PlanExecution(this, Args, Args.SourceFileName);

            ReportProgress(@"Открываем книгу Excel ...");
            
            foreach (var smoData in p.SmoDataList)
                getSmoReportData(smoData);

            totalsReportData = new FomsReportRowData();
            totalsReportData.RowType = FomsReportRowType.Plan;
            totalsReportData.SmoCode = "00000";
            var totalsWs = (Worksheet)p.Workbook.Worksheets[@"00000 стационар"];
            totalsReportData.StacFullCount = totalsWs.Cells.GetCellValue<int>(40, 5) +
                                             totalsWs.Cells.GetCellValue<int>(40, 6);

            totalsReportData.StacDayCount = totalsWs.Cells.GetCellValue<int>(73, 3) +
                                             totalsWs.Cells.GetCellValue<int>(73, 4);

            totalsReportData.StacApuCount = totalsWs.Cells.GetCellValue<int>(107, 3) +
                                             totalsWs.Cells.GetCellValue<int>(107, 4);

            totalsWs = (Worksheet)p.Workbook.Worksheets[@"00000 поликлиника"];
            totalsReportData.ProfilacticCount = totalsWs.Cells.GetCellValue<int>(37, 3) +
                                                totalsWs.Cells.GetCellValue<int>(37, 4);

            totalsReportData.VisitCount = totalsWs.Cells.GetCellValue<int>(37, 5) +
                                             totalsWs.Cells.GetCellValue<int>(37, 6);

            totalsReportData.TreatmentCount = totalsWs.Cells.GetCellValue<int>(37, 7) +
                                             totalsWs.Cells.GetCellValue<int>(37, 8);

            totalsReportData.EmergencyCount = totalsWs.Cells.GetCellValue<int>(42, 5) +
                                             totalsWs.Cells.GetCellValue<int>(42, 6);


            totalsWs = (Worksheet)p.Workbook.Worksheets[@"00000 скорая"];
            totalsReportData.AmbulanceCount = totalsWs.Cells.GetCellValue<int>(6, 3) +
                                             totalsWs.Cells.GetCellValue<int>(8, 3);

            var monthCount = Args.FromBeginOfYear ? Args.Month : 1;
            totalsReportData.StacFullCount = Math.Round(monthCount*totalsReportData.StacFullCount/12, 0);
            totalsReportData.StacDayCount = Math.Round(monthCount * totalsReportData.StacDayCount / 12, 0);
            totalsReportData.StacApuCount = Math.Round(monthCount * totalsReportData.StacApuCount / 12, 0);
            totalsReportData.EmergencyCount = Math.Round(monthCount * totalsReportData.EmergencyCount / 12, 0);
            totalsReportData.ProfilacticCount = Math.Round(monthCount * totalsReportData.ProfilacticCount / 12, 0);
            totalsReportData.VisitCount = Math.Round(monthCount * totalsReportData.VisitCount / 12, 0);
            totalsReportData.TreatmentCount = Math.Round(monthCount * totalsReportData.TreatmentCount / 12, 0);
            totalsReportData.AmbulanceCount = Math.Round(monthCount * totalsReportData.AmbulanceCount / 12, 0);

            reportData.Add(totalsReportData);

            p.Workbook.Close();

            return reportData != null && reportData.Count > 0;
        }

        protected override string getTemplateFileName()
        {
            return "PlanExecutionFomsReport.xls";
        }

        protected override void afterCreateWorkbook()
        {
            Workbook.SetNameValue(@"Период", Utils.GetPeriodName(Args.FromBeginOfYear 
                ? Args.DateBegin.BeginOfYear() : Args.DateBegin, 
                Args.DateEnd));

            using (var dataContext = new VistaMedDataContext(Context))
            {
                var lpu = dataContext.VistaMed.Organisations.FirstOrDefault(x => x.MiacCode == Args.LpuCode);
                if (lpu != null)
                {
                    Workbook.SetNameValue("ЛПУ", lpu.FullName);
                    Workbook.SetNameValue("ФИОРуководителя", lpu.Chief);
                }
            }
            bindSmoDataList(Workbook.Worksheets[1]);
        }

        protected override void afterOpenWorkbook()
        {
            // throw new NotImplementedException();
        }

        /// <summary>
        /// выборка данных для отчета из данных СМО - источника
        /// </summary>
        /// <param name="smoData"></param>
        private void getSmoReportData(PlanExecution.SmoData smoData)
        {
            ReportProgress(@"Выборка данных по СМО {0} ...", smoData.SmoCode);
            addReportRowData(smoData, FomsReportRowType.Plan);
            addReportRowData(smoData, FomsReportRowType.Fact);
        }

        private void addReportRowData(PlanExecution.SmoData smoData, FomsReportRowType rowType)
        {
            var rowData = new FomsReportRowData {SmoCode = smoData.SmoCode, RowType = rowType};

            var isPlanRow = rowType == FomsReportRowType.Plan;
            var isForPeriod = Args.FromBeginOfYear;

            Func<PlanExecution.SmoData.StacionarSectionType, 
                PlanExecution.SmoData.StacionarPokaz, bool, double> stacFunc
                = (section, pokaz, isPlan) =>
                {
                    return smoData.StacionarPage.Sections[section]
                        .ReportRows.Where(x => x.Name.ValueAssigned && !x.Name.Value.Contains(@"в т.ч."))
                        .Select(x => isForPeriod 
                            ? (isPlan ? x.PeriodPlan[pokaz] : x.PeriodFact[pokaz]) 
                            : (isPlan ? x.MonthPlan[pokaz] : x.MonthFact[pokaz]))
                        .Sum(x => x.Adult.Value + x.Child.Value);
                };


            rowData.StacFullCount = stacFunc(PlanExecution.SmoData.StacionarSectionType.Full,
                PlanExecution.SmoData.StacionarPokaz.Kol, isPlanRow);

            rowData.StacDayCount = stacFunc(PlanExecution.SmoData.StacionarSectionType.DayForFull,
                PlanExecution.SmoData.StacionarPokaz.BedDays, isPlanRow);

            rowData.StacApuCount = stacFunc(PlanExecution.SmoData.StacionarSectionType.DayForApu,
                PlanExecution.SmoData.StacionarPokaz.BedDays, isPlanRow);


            Func<PlanExecution.SmoData.PolyclinicSection, PlanExecution.SmoData.PolyclinicPokaz, bool, double> polyFunc
                = (section, pokaz, isPlan) =>
                {
                    return smoData.PolyclinicPage.Sections[section]
                        .ReportRows.Where(x => x.Name.ValueAssigned && !x.Name.Value.Contains(@"в т.ч."))
                        .Select(x => isForPeriod 
                            ? (isPlan ? x.PeriodPlan[pokaz] : x.PeriodFact[pokaz]) 
                            : (isPlan ? x.MonthPlan[pokaz] : x.MonthFact[pokaz]))
                        .Sum(x => x.Adult.Value + x.Child.Value);
                };


            rowData.ProfilacticCount = polyFunc(PlanExecution.SmoData.PolyclinicSection.Doctor,
                    PlanExecution.SmoData.PolyclinicPokaz.Profilactic, isPlanRow) + 
                polyFunc(PlanExecution.SmoData.PolyclinicSection.Dd,
                    PlanExecution.SmoData.PolyclinicPokaz.Profilactic, isPlanRow) +
                polyFunc(PlanExecution.SmoData.PolyclinicSection.Prof,
                    PlanExecution.SmoData.PolyclinicPokaz.Profilactic, isPlanRow);

            rowData.VisitCount = polyFunc(PlanExecution.SmoData.PolyclinicSection.Doctor,
                PlanExecution.SmoData.PolyclinicPokaz.Visit, isPlanRow);

            rowData.TreatmentCount = polyFunc(PlanExecution.SmoData.PolyclinicSection.Doctor,
                PlanExecution.SmoData.PolyclinicPokaz.Obrashenie, isPlanRow);

            rowData.EmergencyCount = polyFunc(PlanExecution.SmoData.PolyclinicSection.Emergency,
                PlanExecution.SmoData.PolyclinicPokaz.Visit, isPlanRow);

            Func<PlanExecution.SmoData.AmbulanceSection, PlanExecution.SmoData.AmbulancePokaz, bool, double> ambulanceFunc
                = (section, pokaz, isPlan) =>
                {
                    return smoData.AmbulancePage.Sections[section].ReportRows
                        .Select(x => isForPeriod 
                            ? (isPlan ? x.PeriodPlan[pokaz] : x.PeriodFact[pokaz]) 
                            : (isPlan ? x.MonthPlan[pokaz] : x.MonthFact[pokaz]))
                        .Sum(x => x.Value);
                };

            rowData.AmbulanceCount = ambulanceFunc(PlanExecution.SmoData.AmbulanceSection.DoctorEvent, 
                    PlanExecution.SmoData.AmbulancePokaz.Kol, isPlanRow) 
                + ambulanceFunc(PlanExecution.SmoData.AmbulanceSection.FeldsherEvent, 
                    PlanExecution.SmoData.AmbulancePokaz.Kol, isPlanRow);

            reportData.Add(rowData);
        }

        public enum FomsReportRowType
        {
            Plan,
            Fact
        }

        public class FomsReportRowData
        {
            public string SmoCode { get; set; }
            public FomsReportRowType RowType { get; set; }
            public double StacFullCount { get; set; }
            public double StacDayCount { get; set; }
            public double EmergencyCount { get; set; }
            public double TreatmentCount { get; set; }
            public double VisitCount { get; set; }
            public double ProfilacticCount { get; set; }
            public double AmbulanceCount { get; set; }
            public double StacApuCount { get; set; }
        }
    }
}
