using NPOI.SS.UserModel;
using Performance.DtoModels;
using Performance.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Performance.Services
{
    public class PerHeaderService : IAutoInjection
    {
        /// <summary>
        /// excel列头转换为矩阵
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        private List<List<int>> ConvertToMatrix(ISheet sheet, PerSheetPoint point)
        {
            point.HeaderFirstRowNum = point.HeaderFirstRowNum.HasValue ? point.HeaderFirstRowNum : sheet.FirstRowNum;
            point.HeaderLastRowNum = point.HeaderLastRowNum.HasValue ? point.HeaderLastRowNum : sheet.LastRowNum;

            List<List<int>> matrix = new List<List<int>>();
            for (int r = point.HeaderFirstRowNum.Value; r < point.HeaderLastRowNum + 1; r++)
            {
                List<int> cell = new List<int>();
                var row = sheet.GetRow(r);

                point.HeaderFirstCellNum = point.HeaderFirstCellNum.HasValue ? point.HeaderFirstCellNum : row.FirstCellNum;

                for (int c = point.HeaderFirstCellNum.Value; c < row.LastCellNum; c++)
                {
                    cell.Add(string.IsNullOrEmpty(row.GetCell(c)?.ToString()) ? 0 : 1);
                }
                matrix.Add(cell);
            }
            return matrix;
        }


        /// <summary>
        /// 递归 获得具有层级关系的列头
        /// </summary>
        /// <param name="cells"></param>
        /// <param name="parentCell"></param>
        /// <param name="pointRow"></param>
        /// <param name="level"></param>
        /// <returns></returns>
        private List<PerHeader> RecursionFill(List<PerHeader> cells, PerHeader parentCell, int pointRow, int level)
        {
            if (cells == null || !cells.Any())
                return null;
            List<PerHeader> sheetHeaders = new List<PerHeader>();
            var groupData = cells.GroupBy(t => t.PointRow);
            //获取起始行号
            if (pointRow <= 0)
            {
                pointRow = groupData.Min(t => t.Key);
                level = 0;
            }
            IEnumerable<PerHeader> child = cells.Where(t => t.PointRow == pointRow);
            //根据合并列的坐标及合并单元格数量，查找子集列头
            if (parentCell != null)
                child = child.Where(t => t.PointCell >= parentCell.PointCell && (t.PointCell + t.MergeCell - 1) <= (parentCell.PointCell + parentCell.MergeCell - 1));

            foreach (var headerCell in child)
            {
                headerCell.Level = level;
                headerCell.Parent = parentCell;
                if (groupData.Any(t => t.Key > pointRow))
                {
                    int nextpoint = groupData.Where(t => t.Key > pointRow).Min(t => t.Key);
                    headerCell.Children = RecursionFill(cells, headerCell, nextpoint, level + 1);
                }

                sheetHeaders.Add(headerCell);
            }
            return sheetHeaders;
        }

        /// <summary>
        /// 读取excel列头
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        public List<PerHeader> GetPerHeader(ISheet sheet, PerSheetPoint point)
        {
            List<List<int>> matrix = ConvertToMatrix(sheet, point);
            var sheetHeaders = ReadHeadMatrix(sheet, point, matrix);
            var result = RecursionFill(sheetHeaders, null, 0, 0);
            return result;
        }

        /// <summary>
        /// 读取excel层级最低列头
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        public List<PerHeader> GetPerHeaderReverse(ISheet sheet, PerSheetPoint point)
        {
            var sheetHeaders = GetPerHeader(sheet, point);
            if (sheetHeaders == null || !sheetHeaders.Any())
                return null;
            return GetPerHeaderReverse(sheetHeaders);
        }

        /// <summary>
        /// 递归 找出层级最低列头
        /// </summary>
        /// <param name="sheetHeaders"></param>
        /// <returns></returns>
        public List<PerHeader> GetPerHeaderReverse(List<PerHeader> sheetHeaders)
        {
            if (sheetHeaders == null || !sheetHeaders.Any())
                return null;

            var headList = new List<PerHeader>();
            foreach (var cell in sheetHeaders)
            {
                if (cell.IsHasChildren)
                {
                    headList.AddRange(GetPerHeaderReverse(cell.Children));
                }
                else
                {
                    headList.Add(cell);
                }
            }
            return headList;
        }

        /// <summary>
        /// 读取excel列头
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        private List<PerHeader> ReadHeadMatrix(ISheet sheet, PerSheetPoint point, List<List<int>> matrix)
        {
            List<PerHeader> headerList = new List<PerHeader>();
            for (int r = 0; r < matrix.Count; r++)
            {
                for (int c = 0; c < matrix[r].Count; c++)
                {
                    int ri = 1, ci = 1;
                    if (matrix[r][c] == 1)
                    {
                        for (int i = r + 1; i < matrix.Count; i++)
                        {
                            if (matrix[i][c] == 1 || matrix[i][c] == -1)
                                break;
                            //var num = matrix[i][c];
                            matrix[i][c] = -1;
                            ri++;
                        }
                        for (int i = c + 1; i < matrix[r].Count; i++)
                        {
                            if (matrix[r][i] == 1 || matrix[r][i] == -1)
                                break;
                            //var num = matrix[r][i];
                            matrix[r][i] = -1;
                            ci++;
                        }
                        var header = new PerHeader
                        {
                            SignID = Guid.NewGuid().ToString("N"),
                            PointRow = point.HeaderFirstRowNum.Value + r,
                            PointCell = point.HeaderFirstCellNum.Value + c,
                            MergeCell = ci,
                            MergeRow = ri,
                            CellValue = sheet.GetRow(point.HeaderFirstRowNum.Value + r)
                                ?.GetCell(point.HeaderFirstCellNum.Value + c)
                                ?.ToString()
                                ?.Trim()
                                ?.RemoveLineBreak()
                        };
                        headerList.Add(header);
                    }
                }
            }
            return headerList;
        }
    }
}
