﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.Office.Interop.Excel;

namespace VistaMedTools
{
    public partial class PlanExecution
    {
        
        
        /// <summary>
        /// Комплексный показатель (показатель по взрослым и детям)
        /// </summary>
        public class AdultChild : ReportValue, IAccumulator<AdultChild>
        {

            public AdultChild(Worksheet worksheet, int rowIndex, int columnIndex)
                : base(worksheet, rowIndex, columnIndex)
            {
                Adult = new DoubleValue(worksheet, rowIndex, columnIndex);
                Child = new DoubleValue(worksheet, rowIndex, columnIndex + 1);
            }

            public override int ColumnCount
            {
                get { return Adult.ColumnCount + Child.ColumnCount; }
            }

            public override bool ValueAssigned
            {
                get { return Adult.ValueAssigned || Child.ValueAssigned; }
            }

            public DoubleValue Adult { get; private set; }
            public DoubleValue Child { get; private set; }

            public void Accumulate(AdultChild value)
            {
                if (value != null)
                {
                    Adult.Accumulate(value.Adult);
                    Child.Accumulate(value.Child);
                }
            }
        }

        public class AdultChildServices : ReportValue, IAccumulator<AdultChildServices>
        {

            public AdultChildServices(Worksheet worksheet, int rowIndex, int columnIndex)
                : base(worksheet, rowIndex, columnIndex)
            {
                AdultServices = new StringValue(worksheet, rowIndex, columnIndex);
                ChildServices = new StringValue(worksheet, rowIndex, columnIndex + 1);
            }

            public override int ColumnCount
            {
                get { return AdultServices.ColumnCount + ChildServices.ColumnCount; }
            }

            public override bool ValueAssigned
            {
                get { return AdultServices.ValueAssigned || ChildServices.ValueAssigned; }
            }

            public StringValue AdultServices { get; private set; }
            public StringValue ChildServices { get; private set; }

            public void Accumulate(AdultChildServices value)
            {
                if (value != null)
                {
                    AdultServices.Accumulate(value.AdultServices);
                    ChildServices.Accumulate(value.ChildServices);
                }
            }
        }

        /// <summary>
        /// лист с данными в составе сведений по СМО
        /// </summary>
        /// <typeparam name="TColumnGroupEnum">перечисление с составом группы показателей</typeparam>
        /// <typeparam name="TReportValue">тип показателей</typeparam>
        /// <typeparam name="TSectionTypeEnum">перечисление с секциями листа</typeparam>
        public abstract partial class DataPage<TColumnGroupEnum, TReportValue, TSectionTypeEnum, TServicesValue> : WorksheetElement
            where TReportValue : ReportValue
            where TServicesValue : ReportValue, IAccumulator<TServicesValue>
        {
            public SmoData SmoData { get; set; }

            public DataPage(Worksheet worksheet, SmoData smoData)
                : base(worksheet)
            {
                SmoData = smoData;
                Sections = new Dictionary<TSectionTypeEnum, PageSection>();
            }

            /// <summary>
            /// секции листа
            /// </summary>
            public Dictionary<TSectionTypeEnum, PageSection> Sections;

            /// <summary>
            /// секция - группа строк на листе
            /// </summary>
            public class PageSection : WorksheetElement
            {
                public TSectionTypeEnum SectionType { get; set; }
                public int MinRowIndex { get; set; }
                public int MaxRowIndex { get; set; }

                public PageSection(Worksheet worksheet, TSectionTypeEnum sectionType,
                    int minRowIndex, int maxRowIndex)
                    : base(worksheet)
                {
                    SectionType = sectionType;
                    MinRowIndex = minRowIndex;
                    MaxRowIndex = maxRowIndex;
                }

                private List<ReportRow> _reportRows = null;
                /// <summary>
                /// строки секции
                /// </summary>
                public List<ReportRow> ReportRows
                {
                    get
                    {
                        if (_reportRows == null)
                        {
                            _reportRows = new List<ReportRow>();
                            for (int i = MinRowIndex; i <= MaxRowIndex; i++)
                            {
                                _reportRows.Add(new ReportRow(Worksheet, i, this));
                            }
                        }
                        return _reportRows;
                    }
                }
            }

            /// <summary>
            /// группа показателей (TColumnGroupEnum - типы показателей в группе, TReportValue - сами показатели)
            /// </summary>
            public class ReportValueGroup : Dictionary<TColumnGroupEnum, TReportValue>
            {
                /// <summary>
                /// строка отчета к которой принадлежит группа
                /// </summary>
                public ReportRow ReportRow { get; private set; }

                /// <summary>
                /// номер начальной колонки группы
                /// </summary>
                public int ColumnIndex { get; private set; }

                public ReportValueGroup(ReportRow reportRow, int columnIndex)
                    : base()
                {
                    ReportRow = reportRow;
                    ColumnIndex = columnIndex;

                    // создаем показатели в группе
                    var cci = columnIndex;
                    foreach (var enumValue in Enum.GetValues(typeof(TColumnGroupEnum)).Cast<TColumnGroupEnum>())
                    {
                        var reportValue = CreateReportValue(cci);
                        Add(enumValue, reportValue);
                        cci += reportValue.ColumnCount;
                    }
                }

                /// <summary>
                /// кеш конструкторов показателей
                /// </summary>
                private static readonly Dictionary<Type, ConstructorInfo> constructors
                    = new Dictionary<Type, ConstructorInfo>();

                /// <summary>
                /// создать показатель для группы
                /// </summary>
                /// <param name="colIdnex"></param>
                /// <returns></returns>
                public TReportValue CreateReportValue(int colIdnex)
                {
                    var t = typeof(TReportValue);
                    if (!constructors.ContainsKey(t))
                    {
                        var typeConstructor =
                            t.GetConstructor(new Type[] { typeof(Worksheet), typeof(int), typeof(int) });
                        if (typeConstructor != null)
                            constructors.Add(t, typeConstructor);
                    }

                    var result = default(TReportValue);
                    if (constructors.ContainsKey(t))
                        result = (TReportValue)constructors[t]
                            .Invoke(new object[] { ReportRow.Worksheet, ReportRow.RowIndex, colIdnex });

                    return result;
                }

                /// <summary>
                /// ширина группы в колонках
                /// </summary>
                public int ColumnCount
                {
                    get { return Values.Sum(x => x.ColumnCount); }
                }
            }

            public class ServicesGroup : Dictionary<TColumnGroupEnum, TServicesValue>
            {
                /// <summary>
                /// строка отчета к которой принадлежит группа
                /// </summary>
                public ReportRow ReportRow { get; private set; }

                /// <summary>
                /// номер начальной колонки группы
                /// </summary>
                public int ColumnIndex { get; private set; }

                public ServicesGroup(ReportRow reportRow, int columnIndex)
                    : base()
                {
                    ReportRow = reportRow;
                    ColumnIndex = columnIndex;

                    // создаем показатели в группе
                    var cci = columnIndex;
                    foreach (var enumValue in Enum.GetValues(typeof(TColumnGroupEnum)).Cast<TColumnGroupEnum>())
                    {
                        var reportValue = CreateReportValue(cci);
                        Add(enumValue, reportValue);
                        cci += reportValue.ColumnCount;
                    }
                }

                /// <summary>
                /// кеш конструкторов показателей
                /// </summary>
                private static readonly Dictionary<Type, ConstructorInfo> constructors
                    = new Dictionary<Type, ConstructorInfo>();

                /// <summary>
                /// создать показатель для группы
                /// </summary>
                /// <param name="colIdnex"></param>
                /// <returns></returns>
                public TServicesValue CreateReportValue(int colIdnex)
                {
                    var t = typeof(TServicesValue);
                    if (!constructors.ContainsKey(t))
                    {
                        var typeConstructor =
                            t.GetConstructor(new Type[] { typeof(Worksheet), typeof(int), typeof(int) });
                        if (typeConstructor != null)
                            constructors.Add(t, typeConstructor);
                    }

                    var result = default(TServicesValue);
                    if (constructors.ContainsKey(t))
                        result = (TServicesValue)constructors[t]
                            .Invoke(new object[] { ReportRow.Worksheet, ReportRow.RowIndex, colIdnex });

                    return result;
                }

                /// <summary>
                /// ширина группы в колонках
                /// </summary>
                public int ColumnCount
                {
                    get { return Values.Sum(x => x.ColumnCount); }
                }
            }

            /// <summary>
            /// строка в составе секции
            /// </summary>
            public class ReportRow : WorksheetElement
            {
                public ReportRow(Worksheet worksheet, int rowIndex, PageSection pageSection)
                    : base(worksheet)
                {
                    RowIndex = rowIndex;
                    PageSection = pageSection;

                    var cci = 1;

                    Codes = new StringValue(worksheet, rowIndex, cci);
                    cci += Codes.ColumnCount;

                    Name = new StringValue(worksheet, rowIndex, cci);
                    cci += Name.ColumnCount;

                    YearPlan = new ReportValueGroup(this, cci);
                    cci += YearPlan.ColumnCount;

                    MonthPlan = new ReportValueGroup(this, cci);
                    cci += MonthPlan.ColumnCount;

                    MonthFact = new ReportValueGroup(this, cci);
                    cci += MonthFact.ColumnCount;

                    MonthDelta = new ReportValueGroup(this, cci);
                    cci += MonthDelta.ColumnCount;

                    PeriodPlan = new ReportValueGroup(this, cci);
                    cci += PeriodPlan.ColumnCount;

                    PeriodFact = new ReportValueGroup(this, cci);
                    cci += PeriodFact.ColumnCount;

                    PeriodDelta = new ReportValueGroup(this, cci);
                    cci += PeriodDelta.ColumnCount;

                    FactServices = new ServicesGroup(this, cci);
                    cci += PeriodDelta.ColumnCount;
                }

                /// <summary>
                /// номер строки отчета
                /// </summary>
                public int RowIndex { get; private set; }

                public PageSection PageSection { get; set; }

                /// <summary>
                /// коды строки (коды услуг или фед.коды специальностей врачей)
                /// </summary>
                public StringValue Codes { get; set; }

                /// <summary>
                /// наименование строки (профиль койко-мест или шаблоны специальности врачей)
                /// </summary>
                public StringValue Name { get; private set; }

                /// <summary>
                /// групппа показателей годовой план
                /// </summary>
                public ReportValueGroup YearPlan { get; set; }

                /// <summary>
                /// группа показателей месячный план
                /// </summary>
                public ReportValueGroup MonthPlan { get; set; }

                /// <summary>
                /// группа показателей исполнение месячного плана
                /// </summary>
                public ReportValueGroup MonthFact { get; set; }

                /// <summary>
                /// группа показателей отклонения в выполнении месячного плана
                /// </summary>
                public ReportValueGroup MonthDelta { get; set; }

                /// <summary>
                /// группа показателей план с начала года
                /// </summary>
                public ReportValueGroup PeriodPlan { get; set; }

                /// <summary>
                /// группа показателей исполнение плана с начала года
                /// </summary>
                public ReportValueGroup PeriodFact { get; set; }

                /// <summary>
                /// группа показателей отклонения в выполнении плана с начала года
                /// </summary>
                public ReportValueGroup PeriodDelta { get; set; }

                /// <summary>
                /// Список услуг задействованных при фактическом исполнеии плана по строке
                /// </summary>
                public ServicesGroup FactServices { get; set; }
            }
        }
    }
}
