using FLY.Thick.FilmCasting.Server.Model;
using FLY.Thick.FilmCasting.UI.DbViewer.Db;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using Misc;
using OfficeOpenXml;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Style;
using OfficeOpenXml.Table;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace FLY.Thick.FilmCasting.UI.DbViewer.Core
{
    public class ExcelOuput : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public double Progress {
            get {
                if (TotalRow > 0)
                {
                    if (CurrRow < TotalRow)
                    {
                        return 1.0 * CurrRow / TotalRow;
                    }
                    else
                    {
                        return 1;
                    }
                }
                else {
                    return 1;
                }
            }
        }
        public string Msg { get; private set; }

        protected int TotalRow { get; private set; } = 1;
        protected int CurrRow { get; private set; }
        const int CurrRowGrowUpStep = 500;
        public async Task SaveToXlsx(string filepath, DbProfilePack profilePack)
        {
            await SaveToXlsxNoTemplate(filepath, profilePack);
        }

        /// <summary>
        /// 没有模板导出数据
        /// </summary>
        /// <param name="filepath"></param>
        async Task SaveToXlsxNoTemplate(string filepath, DbProfilePack profilePack)
        {
            await Task.Run(() =>
            {
                //生成模板
                GeneratedCode.GeneratedClass generatedClass = new GeneratedCode.GeneratedClass();
                generatedClass.CreatePackage(filepath);

                //修改模板
                using (ExcelPackage p = new ExcelPackage(new FileInfo(filepath)))
                {

                    SaveToXlsxeStatistics(p, profilePack);
                    SaveToXlsxThkDatas(p, profilePack);
                    SaveToXlsxThkHistogram(p, profilePack);

                    Msg = "写入文件";
                    p.Save();

                }
            });
        }

        
        ExcelWorksheet GetSheet(ExcelPackage p, string sheet_name)
        {

            ExcelWorksheet sheet = p.Workbook.Worksheets[sheet_name];
            if (sheet == null)
            {
                sheet = p.Workbook.Worksheets.Add(sheet_name);
            }
            return sheet;
        }

        void SaveToXlsxeStatistics_trend(ExcelWorksheet sheet, DbProfilePack profilePack) 
        {
            int fromRowNumber = 2;
            int toRowNumber = profilePack.ScanDatasMix.Count() + 1;

            string timeAddress = $"扫描数据!A{fromRowNumber}:A{toRowNumber}";
            string thkDataAddress = $"扫描数据!N{fromRowNumber}:N{toRowNumber}";
            string targetAddress = $"扫描数据!C{fromRowNumber}:C{toRowNumber}";

            ExcelChart chart = (ExcelChart)sheet.Drawings["均值趋势图"];
            int count = chart.Series.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                chart.Series.Delete(i);
            }

            {
                var chartSeries = chart.Series.Add(thkDataAddress,timeAddress);
                chartSeries.HeaderAddress = new ExcelAddressBase("扫描数据!N1");
            }

            {
                var chartSeries = chart.Series.Add(targetAddress, timeAddress);
                chartSeries.HeaderAddress = new ExcelAddressBase("扫描数据!C1");
            }

            //范围
            chart.YAxis.MaxValue = profilePack.YMax;
            chart.YAxis.MinValue = profilePack.YMin;
            chart.YAxis.MajorUnit = profilePack.Tolerance;



            sheet.Cells["B26"].Formula = $"AVERAGE({thkDataAddress})";
            sheet.Cells["D26"].Formula = $"STDEV({thkDataAddress})*2";
            sheet.Cells["F26"].Formula = $"MIN({thkDataAddress})";
            sheet.Cells["H26"].Formula = $"MAX({thkDataAddress})";
        }
        void SaveToXlsxeStatistics_scan(ExcelWorksheet sheet, DbProfilePack profilePack)
        {
            int boltCnt = profilePack.NBolts;
            //扫描数据表最后一行
            int scanDataEndRowNumber = GetRowNumberOfScanDataTableEnd(profilePack);

            string boltAddress;
            string avgAddress;

            //扫描数据平均值表第一行(螺丝号)
            int from_rowNumber = GetRowNumberOfAvgTableBegin(profilePack);
            //扫描数据平均值表第一列(螺丝号)
            int from_colNumber = GetColNumberOfAvgTableBegin(profilePack);



            int to_rowNumber = from_rowNumber + 1;
            int to_colNumber = from_colNumber + boltCnt;

            boltAddress = $"扫描数据!{new ExcelAddress(from_rowNumber, from_colNumber + 1, from_rowNumber, to_colNumber).Address}";
            avgAddress = $"扫描数据!{new ExcelAddress(to_rowNumber, from_colNumber + 1, to_rowNumber, to_colNumber).Address}";
            

            //警告：
            //上面，不能使用 new ExcelAddressBase("扫描数据", to_rowNumber, from_colNumber + 1, to_rowNumber, to_colNumber).Address
            //这样有bug, 导出的字符串，没有 表名 "扫描数据!"


            ExcelChart chart = (ExcelChart)sheet.Drawings["横向扫描图"];
            int count = chart.Series.Count;
            for (int i = count - 1; i >= 0; i--) {
                chart.Series.Delete(i);
            }

            {
                var chartSeries = chart.Series.Add(avgAddress, boltAddress);
                chartSeries.HeaderAddress = new ExcelAddressBase("扫描数据", to_rowNumber, from_colNumber, to_rowNumber, from_colNumber);
            }
            //添加5条曲线
            if (profilePack.ScanDatasMix.Count() > 5)
            {
                for (int i = 0; i < 5; i++)
                {
                    int rowNumber = 1 + 1 + (profilePack.ScanDatasMix.Count() - 1) - (i * profilePack.ScanDatasMix.Count()/5);


                    string dataAddress = $"扫描数据!{new ExcelAddress(rowNumber, from_colNumber + 1, rowNumber, to_colNumber).Address}";
                    var chartSeries = chart.Series.Add(dataAddress, boltAddress);
                    chartSeries.HeaderAddress = new ExcelAddressBase("扫描数据", rowNumber, 1, rowNumber, 1);
                }
            }
            else
            {
                //小于5条
                for (int i = 0; i < profilePack.ScanDatasMix.Count(); i++)
                {

                    int rowNumber = 1 + 1 + (profilePack.ScanDatasMix.Count() -1)- i;

                    string dataAddress = $"扫描数据!{new ExcelAddress(rowNumber, from_colNumber + 1, rowNumber, to_colNumber).Address}";
                    var chartSeries = chart.Series.Add(dataAddress, boltAddress);
                    chartSeries.HeaderAddress = new ExcelAddressBase("扫描数据", rowNumber, 1, rowNumber, 1);
                }
            }

            //范围
            chart.YAxis.MaxValue = profilePack.YMax;
            chart.YAxis.MinValue = profilePack.YMin;
            chart.YAxis.MajorUnit = profilePack.Tolerance;

            {
                int from_rowNumber2 = to_rowNumber;
                int to_rowNumber2 = to_rowNumber;
                int from_colNumber2 = from_colNumber + 1 + profilePack.DataBegin;
                int to_colNumber2 = from_colNumber + 1 + profilePack.DataEnd;

                string stateAddress = $"扫描数据!{new ExcelAddress(from_rowNumber2, from_colNumber2, to_rowNumber2, to_colNumber2).Address}";

                sheet.Cells["B47"].Formula = $"AVERAGE({stateAddress})";
                sheet.Cells["D47"].Formula = $"STDEV({stateAddress})*2";
                sheet.Cells["F47"].Formula = $"MIN({stateAddress})";
                sheet.Cells["H47"].Formula = $"MAX({stateAddress})";
            }
        }

        void SaveToXlsxeStatistics_histogram(ExcelWorksheet sheet, DbProfilePack profilePack)
        {
            int fromRowNumber = 2;
            int toRowNumber = profilePack.Histogram.Count() + 1;

            string thkAddress = $"厚度直方图!A{fromRowNumber}:A{toRowNumber}";
            string freqAddress = $"厚度直方图!C{fromRowNumber}:C{toRowNumber}";
            string normAddress = $"厚度直方图!D{fromRowNumber}:D{toRowNumber}";

            ExcelChart chart = (ExcelChart)sheet.Drawings["厚度直方图"];
            int count = chart.Series.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                chart.Series.Delete(i);
            }

            {
                var chartSeries = chart.Series.Add(freqAddress, thkAddress);
                chartSeries.HeaderAddress = new ExcelAddressBase("厚度直方图!C1");
            }

            {
                var chartSeries = chart.Series.Add(normAddress, thkAddress);
                chartSeries.HeaderAddress = new ExcelAddressBase("厚度直方图!D1");
            }

            //范围
            chart.XAxis.MaxValue = profilePack.YMax;
            chart.XAxis.MinValue = profilePack.YMin;

            sheet.Cells["B69"].Formula = $"厚度直方图!G2";
            sheet.Cells["D69"].Formula = $"厚度直方图!F2";
            sheet.Cells["F69"].Formula = $"厚度直方图!H2";
        }
        void SaveToXlsxeStatistics(ExcelPackage p, DbProfilePack profilePack)
        {
            ExcelWorksheet sheet = GetSheet(p, "总体描述");

            sheet.Cells["C2"].Value = profilePack.Profile.PName;

            sheet.Cells["H2"].Value = profilePack.Profile.Batch;

            sheet.Cells["C3"].Value = profilePack.Profile.Number;

            sheet.Cells["F3"].Value = profilePack.ScanDatasMix.Count();

            sheet.Cells["I3"].Value = profilePack.Profile.FilmLength;

            sheet.Cells["C4"].Value = profilePack.Profile.StartTime.ToOADate();

            sheet.Cells["G4"].Value = profilePack.Profile.EndTime.ToOADate();

            sheet.Cells["C5"].Value = profilePack.Target;

            sheet.Cells["F5"].Value = profilePack.TolerancePercent * 100;

            sheet.Cells["I5"].Formula = "C5*F5/100";

            sheet.Cells["C6"].Formula = "C5+I5";

            sheet.Cells["F6"].Formula = "C5-I5";

            sheet.Cells["I6"].Value = profilePack.InTolerancePercentRange * 100.0;

            int from_rowNumber = 2;
            int to_rowNumber = GetRowNumberOfScanDataTableEnd(profilePack);
            //扫描数据 "螺丝1(um)"列为 16 
            int from_colNumber = ColNumberOfBoltNo1 + profilePack.DataBegin;
            int to_colNumber = ColNumberOfBoltNo1 + profilePack.DataEnd;

            string thkDataAddress = $"扫描数据!{new ExcelAddress(from_rowNumber, from_colNumber, to_rowNumber, to_colNumber).Address}";

            sheet.Cells["C7"].Formula = $"AVERAGE({thkDataAddress})";
            sheet.Cells["I7"].Formula = $"STDEV({thkDataAddress})*2";
            sheet.Cells["F7"].Formula = $"I7/C7*100";
            string formula_avg = "(C7)";
            string formula_sigma3 = "(I7/2*3)";
            string formula_ul = "(C6)";
            string formula_ll = "(F7)";
            string formula_cpku = $"(ABS({formula_ul}-{formula_avg})/{formula_sigma3})";
            string formula_cpkl = $"(ABS({formula_ll}-{formula_avg})/{formula_sigma3})";
            sheet.Cells["C8"].Formula = $"MIN({formula_cpku},{formula_cpkl})";


            SaveToXlsxeStatistics_trend(sheet, profilePack);
            SaveToXlsxeStatistics_scan(sheet, profilePack);
            SaveToXlsxeStatistics_histogram(sheet, profilePack);
        }

        void SaveToXlsxThkDatas(ExcelPackage p, DbProfilePack profilePack)
        {
            Msg = "导出扫描数据记录";
            int boltCnt = profilePack.NBolts;
            int sampleCnt = 0;
            {
                var scanData = profilePack.ScanDatasMix.Last();
                if (scanData.SampleADs != null && scanData.SampleADs.Count() > 0)
                    sampleCnt = scanData.SampleADs.Count();
            }

            var columnInfos = new List<ColumnInfo<Lc_ScanData2>>
            {
                new ColumnInfo<Lc_ScanData2>("开始时间", typeof(double), "yyyy-MM-dd HH:mm:ss", (sender)=>sender.Time.ToOADate()),
                new ColumnInfo<Lc_ScanData2>("结束时间", typeof(double), "yyyy-MM-dd HH:mm:ss", (sender)=>sender.EndTime.ToOADate()),
                new ColumnInfo<Lc_ScanData2>("目标值(um)", typeof(double), "0.00", (sender)=>sender.Target),
                new ColumnInfo<Lc_ScanData2>("公差(%)", typeof(double), "0.0", (sender)=>sender.TolerancePercent*100),
                new ColumnInfo<Lc_ScanData2>("公差(um)", typeof(double), "0.00", (sender)=>sender.Target*sender.TolerancePercent),
                new ColumnInfo<Lc_ScanData2>("斜率补偿", typeof(double), "0.000", (sender)=>sender.K),
                new ColumnInfo<Lc_ScanData2>("平移补偿", typeof(double), "0.00", (sender)=>sender.B),

                new ColumnInfo<Lc_ScanData2>("纵向位置(m)", typeof(double), "0.00", (sender)=>sender.FilmPosition),
                new ColumnInfo<Lc_ScanData2>("生产速度(m/min)", typeof(double), "0.0" , (sender)=>sender.FilmVelocity),
                new ColumnInfo<Lc_ScanData2>("拉伸前速度(m/min)", typeof(double), "0.0" , (sender)=>sender.FilmVelocityBeginStretching),
                new ColumnInfo<Lc_ScanData2>("膜宽度(mm)", typeof(double), "0" , (sender)=>sender.FilmWidth),
                new ColumnInfo<Lc_ScanData2>("数据范围开始", typeof(double), "0" , (sender)=>sender.DataBegin+1),
                new ColumnInfo<Lc_ScanData2>("数据范围结束", typeof(double), "0" , (sender)=>sender.DataEnd+1),

                new ColumnInfo<Lc_ScanData2>("平均值(um)", typeof(double), "0.00" , (sender)=>sender.Avg),
                new ColumnInfo<Lc_ScanData2>("2σ(%)", typeof(double), "0.0", (sender)=>{

                    double avg = sender.Avg;
                    double sigma2 = sender.Sigma2;

                    if (double.IsNaN(avg) || avg<=0 || double.IsNaN(sigma2))
                    {
                        return double.NaN;
                    }
                    else{
                        double sigma2p = sigma2/avg * 100;
                        return sigma2p;
                    }
                }),

                new ColumnInfo<Lc_ScanData2>((i)=>$"螺丝{i + 1}(um)", typeof(double), "0.00" ,boltCnt,(sender,i)=>{
                    if(i<sender.Thicks.Count())
                        return sender.Thicks[i];
                    else
                        return null;
                }),
                new ColumnInfo<Lc_ScanData2>((i)=>$"样品{i}AD", typeof(double), "0" ,sampleCnt,(sender,i)=>{
                    if(sender.SampleADs!=null &&  i<sender.SampleADs.Count())
                        return sender.SampleADs[i];
                    else
                        return null;
                }),
                new ColumnInfo<Lc_ScanData2>("螺丝宽度(mm)", typeof(double), "0.0" ,(sender)=>{
                    if(sender.BoltMapInfo!=null)
                        return sender.BoltMapInfo.BoltWidth;
                    return null;
                }),
                new ColumnInfo<Lc_ScanData2>("总螺丝数", typeof(double), "0" ,(sender)=>{
                    if(sender.BoltMapInfo!=null)
                        return sender.BoltMapInfo.BoltCnt;
                    return null;
                }),
                new ColumnInfo<Lc_ScanData2>("左不工作螺丝数", typeof(double), "0" ,(sender)=>{
                    if(sender.BoltMapInfo!=null)
                        return sender.BoltMapInfo.LeftEmptyBolts;
                    return null;
                }),
                new ColumnInfo<Lc_ScanData2>("右不工作螺丝数", typeof(double), "0" ,(sender)=>{
                    if(sender.BoltMapInfo!=null)
                        return sender.BoltMapInfo.RightEmptyBolts;
                    return null;
                }),
                new ColumnInfo<Lc_ScanData2>("收缩率(%)", typeof(double), "0.0" ,(sender)=>{
                    if(sender.BoltMapInfo!=null)
                        return sender.BoltMapInfo.UniformShrinkPercent * 100;
                    return null;
                }),
                new ColumnInfo<Lc_ScanData2>("中心偏移(mm)", typeof(double), "0" ,(sender)=>{
                    if(sender.BoltMapInfo!=null)
                        return sender.BoltMapInfo.MidOffset;
                    return null;
                }),
            };

            columnInfos.UpdateColumnIdx();

            CurrRowInit(profilePack.ScanDatasMix.Count());

            CurrRowGrowUpReady();

            ExcelWorksheet sheet = GetSheet(p, "扫描数据");
            sheet.Cells.Clear();

            columnInfos.ToSheet(sheet, profilePack.ScanDatasMix, CurrRowGrowUping);

            CurrRowGrowUpFinish(profilePack.ScanDatasMix.Count());

            //最后加入平均值
            //公式
            SaveToXlsxThkDatas_hAvgData(sheet, profilePack);

            

        }

        /// <summary>
        /// 找到写着 "螺丝1(um)" 的那列
        /// </summary>
        const int ColNumberOfBoltNo1 = 16;

        /// <summary>
        /// 扫描数据最后一行
        /// </summary>
        /// <param name="profilePack"></param>
        /// <returns></returns>
        int GetRowNumberOfScanDataTableEnd(DbProfilePack profilePack) 
        {
            int scanDataEndRowNumber = profilePack.ScanDatasMix.Count() + 1;
            return scanDataEndRowNumber;
        }
        /// <summary>
        /// 扫描数据中的平均值表的第1行
        /// </summary>
        /// <param name="profilePack"></param>
        /// <returns></returns>
        int GetRowNumberOfAvgTableBegin(DbProfilePack profilePack) 
        {
            int scanDataEndRowNumber = GetRowNumberOfScanDataTableEnd(profilePack);
            int from_rowNumber = scanDataEndRowNumber + 1 + 1;
            return from_rowNumber;
        }
        /// <summary>
        /// 扫描数据中的平均值表的第1列
        /// </summary>
        /// <param name="profilePack"></param>
        /// <returns></returns>
        int GetColNumberOfAvgTableBegin(DbProfilePack profilePack)
        {
            int from_colNumber = ColNumberOfBoltNo1 - 1;
            return from_colNumber;
        }

        void SaveToXlsxThkDatas_hAvgData(ExcelWorksheet sheet, DbProfilePack profilePack) 
        {
            int boltCnt = profilePack.NBolts;

            //扫描数据表最后一行
            int scanDataEndRowNumber = GetRowNumberOfScanDataTableEnd(profilePack);

            //横向均值表 与 扫描数据表，隔了一行
            int from_rowNumber = GetRowNumberOfAvgTableBegin(profilePack);

            //找到写着 "螺丝1(um)" 的那列 的 左边 
            int from_colNumber = GetColNumberOfAvgTableBegin(profilePack);

            //标题
            sheet.Cells[from_rowNumber, from_colNumber].Value = "螺丝号";
            sheet.Cells[from_rowNumber + 1, from_colNumber].Value = "横向均值(um)";

            for (int i = 0; i < boltCnt; i++)
            {
                int rowNumber = from_rowNumber;
                int colNumber = from_colNumber + 1 + i;
                sheet.Cells[rowNumber, colNumber].Value = i + 1;
                sheet.Cells[rowNumber, colNumber].Style.Numberformat.Format = "0";
                sheet.Cells[rowNumber + 1, colNumber].Formula = $"AVERAGE({new ExcelAddress(2, colNumber, scanDataEndRowNumber, colNumber).Address})";
                sheet.Cells[rowNumber + 1, colNumber].Style.Numberformat.Format = "0.00";
            }

            using (var range = sheet.Cells[from_rowNumber, from_colNumber, from_rowNumber + 1, from_colNumber + boltCnt])
            {
                range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
            }

            using (var range = sheet.Cells[from_rowNumber, from_colNumber, from_rowNumber, from_colNumber + boltCnt])
            {
                range.Style.Fill.PatternType = ExcelFillStyle.Solid;
                range.Style.Fill.BackgroundColor.SetColor(Color.Yellow);
            }
        }
        void SaveToXlsxThkHistogram(ExcelPackage p, DbProfilePack profilePack)
        {
            Msg = "导出厚度直方图";

            var columnInfos = new List<ColumnInfo<DistCell>>
            {
                new ColumnInfo<DistCell>("厚度(um)", typeof(double), "0", (sender)=>sender.Thick),
                new ColumnInfo<DistCell>("数量", typeof(double), "0", (sender)=>sender.Count),
                new ColumnInfo<DistCell>("频数(%)", typeof(double), "0.00", (sender)=>sender.Freq100),
                new ColumnInfo<DistCell>("正态分布(%)", typeof(double), "0.00", (sender)=>sender.Norm100)
            };
            columnInfos.UpdateColumnIdx();
            CurrRowGrowUpReady();
            
            ExcelWorksheet sheet = GetSheet(p, "厚度直方图");
            sheet.Cells.Clear();
            columnInfos.ToSheet(sheet, profilePack.Histogram, CurrRowGrowUping);

            CurrRowGrowUpFinish(profilePack.Histogram.Count());

            sheet.Cells["F1"].Value = "2σ(um)";
            sheet.Cells["F2"].Value = profilePack.Histogram_Sigma * 2;
            sheet.Cells["F2"].Style.Numberformat.Format = "0.0";

            sheet.Cells["G1"].Value = "平均(um)";
            sheet.Cells["G2"].Value = profilePack.Histogram_Average;
            sheet.Cells["G2"].Style.Numberformat.Format = "0.0";

            sheet.Cells["H1"].Value = "统计数";
            sheet.Cells["H2"].Value = profilePack.Histogram_TotalCount;

            using (var range = sheet.Cells["F1:H1"])
            {
                range.Style.Fill.PatternType = ExcelFillStyle.Solid;
                range.Style.Fill.BackgroundColor.SetColor(Color.Yellow);
            }
            using (var range = sheet.Cells["F1:H2"])
            {
                range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
            }
        }



        void CurrRowInit(int totalRow)
        {
            TotalRow = totalRow;
            CurrRow = 0;
        }
        int _currRow = 0;
        void CurrRowGrowUpReady()
        {
            _currRow = CurrRow;
        }
        void CurrRowGrowUping(int i)
        {
            if (i % CurrRowGrowUpStep == 0)
                CurrRow = _currRow + i;
        }
        void CurrRowGrowUpFinish(int cnt)
        {
            CurrRow = _currRow + cnt;
        }


    }
   
    public class ColumnInfo<T>
    {
        public string ColumnName;
        public Type DataType;
        public string Format;
        public delegate object GetValueHanlder(T sender, int itemIdx);
        GetValueHanlder getValue;
        public bool IsArray = false;
        public int ItemCount = 1;
        public delegate string GetColumnNameHanlder(int itemIdx);
        GetColumnNameHanlder getColumnName;
        //相对于整个表，列序号，从0开始数
        public int ColumnIdx;

        public ColumnInfo(string columnName, Type dataType, string format, Func<T, object> getValue)
        {
            ColumnName = columnName;
            DataType = dataType;
            Format = format;
            this.getValue = (s, i) => getValue(s);
        }
        public ColumnInfo(string columnName, Type dataType, string format, GetValueHanlder getValue)
        {
            ColumnName = columnName;
            DataType = dataType;
            Format = format;
            this.getValue = getValue;
            IsArray = false;
            ItemCount = 1;
        }
        public ColumnInfo(GetColumnNameHanlder getColumnName, Type dataType, string format, int itemCount, GetValueHanlder getValue)
        {
            IsArray = true;
            ItemCount = itemCount;
            this.getColumnName = getColumnName;
            DataType = dataType;
            Format = format;
            this.getValue = getValue;
        }

        public object GetValue(T sender)
        {
            return getValue(sender, 0);
        }
        public object GetValue(T sender, int index)
        {
            return getValue(sender, index);
        }
        public string GetColumnName(int index)
        {
            return getColumnName(index);
        }
        public string GetColumnName()
        {
            return ColumnName;
        }
        public int GetColumnIdx(int itemIdx)
        {
            return ColumnIdx + itemIdx;
        }
        public int GetColumnIdx()
        {
            return ColumnIdx;
        }

    }
    public static class ColumnInfoExt
    {
        public static void UpdateColumnIdx<T>(this List<ColumnInfo<T>> columnInfos)
        {
            int colIndex = 0;
            for (int i = 0; i < columnInfos.Count(); i++)
            {
                var columnInfo = columnInfos[i];
                columnInfo.ColumnIdx = colIndex;
                colIndex += columnInfo.ItemCount;
            }
        }
        public static void ToSheet<T>(this List<ColumnInfo<T>> columnInfos, ExcelWorksheet sheet, IEnumerable<T> senders, Action<int> progressEvent = null)
        {
            int progress = 0;

            int from_rowNumber = 1;
            int rowNumber = from_rowNumber;
            int from_colNumber = 1;
            int to_rowNumber = 1;
            int to_colNumber = 1;
            //添加标题
            for (int i = 0; i < columnInfos.Count(); i++)
            {
                var columnInfo = columnInfos[i];
                if (!columnInfo.IsArray)
                {
                    int colNumber = from_colNumber + columnInfo.GetColumnIdx();

                    sheet.Cells[rowNumber, colNumber].Value = columnInfo.GetColumnName();
                    sheet.Column(colNumber).Style.Numberformat.Format = columnInfo.Format;

                    if (to_colNumber < colNumber)
                        to_colNumber = colNumber;
                }
                else
                {
                    for (int k = 0; k < columnInfo.ItemCount; k++)
                    {
                        int colNumber = from_colNumber + columnInfo.GetColumnIdx(k);
                        sheet.Cells[rowNumber, colNumber].Value = columnInfo.GetColumnName(k);
                        sheet.Column(colNumber).Style.Numberformat.Format = columnInfo.Format;

                        if (to_colNumber < colNumber)
                            to_colNumber = colNumber;
                    }
                }
            }
            rowNumber++;
            progressEvent?.Invoke(progress++);


            foreach (var sender in senders)
            {
                for (int i = 0; i < columnInfos.Count(); i++)
                {
                    var columnInfo = columnInfos[i];
                    if (!columnInfo.IsArray)
                    {
                        int colNumber = from_colNumber + columnInfo.GetColumnIdx();
                        var value = columnInfo.GetValue(sender);
                        if (value != null)
                        {
                            sheet.Cells[rowNumber, colNumber].Value = value;
                        }
                    }
                    else
                    {
                        for (int k = 0; k < columnInfo.ItemCount; k++)
                        {
                            int colNumber = from_colNumber + columnInfo.GetColumnIdx(k);
                            var value = columnInfo.GetValue(sender, k);
                            if (value != null)
                            {
                                sheet.Cells[rowNumber, colNumber].Value = value;
                            }
                        }
                    }

                }
                rowNumber++;
                progressEvent?.Invoke(progress++);
            }
            to_rowNumber = rowNumber - 1;


            using (var range = sheet.Cells[from_rowNumber, from_colNumber, to_rowNumber, to_colNumber])
            {
                range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                range.Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
            }

            using (var range = sheet.Cells[from_rowNumber, from_colNumber, from_rowNumber, to_colNumber])
            {
                range.Style.Fill.PatternType = ExcelFillStyle.Solid;
                range.Style.Fill.BackgroundColor.SetColor(Color.Yellow);
                range.AutoFitColumns();
            }
        }
    }
}
