/*
<copyright file="BGExcelImageProcessorExport.cs" company="BansheeGz">
    Copyright (c) 2018-2024 All Rights Reserved
</copyright>
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using NPOI;
using NPOI.OpenXmlFormats.Dml.Spreadsheet;
using NPOI.SS.UserModel;
using NPOI.Util;
using NPOI.XSSF.UserModel;
using UnityEngine;

namespace BansheeGz.BGDatabase.Editor
{
    public class BGExcelImageProcessorExport
    {
        public void Process(BGExcelImageProcessorExportRequest request)
        {
            var RELATIONS = BGPrivate.GetField<Dictionary<int, POIXMLRelation>>(typeof(XSSFPictureData), "RELATIONS");
            var methodInfo = BGPrivate.GetMethod(request.book, "CreateRelationship", new Type[]
            {
                typeof(POIXMLRelation),
                typeof(POIXMLFactory), typeof(int), typeof(bool)
            });

            request.assetsConfig.ForEachImageField(request.repo, true, field =>
            {
                var sheetInfo = request.bookInfo.GetEntitySheet(field.MetaId);
                if (sheetInfo == null) return;
                var column = sheetInfo.GetFieldColumn(field.Id);
                if (column < 0) return;
                var sheet = (XSSFSheet)request.book.GetSheetAt(sheetInfo.SheetNumber);

                var addedCount = 0;
                var imageField = new BGExcelImageField(field);
                var drawing = sheet.CreateDrawingPatriarch();
                field.Meta.ForEachEntity(entity =>
                {
                    var row = sheetInfo.GetRow(entity.Id);
                    if (row < 0) return;
                    if (!imageField.HasValue(entity.Index)) return;
                    var asset = new BGExcelImageAsset(imageField, entity);
                    if (!asset.IsValid) return;

                    try
                    {
                        //make sure the cell exists
                        var excelRow = sheet.GetRow(row) ?? sheet.CreateRow(row);
                        if (excelRow.GetCell(column) == null) excelRow.CreateCell(column);

                        //create anchor
                        var anchor = (XSSFClientAnchor)drawing.CreateAnchor(0, 0, 0, 0, column, row, column, row);

                        //add picture
                        // var pictureIndex = sheet.Workbook.AddPicture(asset.Content, asset.pictureType);
                        var pictureIndex = AddPicture(request, asset.Content, asset.pictureType, RELATIONS, methodInfo);

                        //add picture representation to anchor 
                        var picture = drawing.CreatePicture(anchor, pictureIndex);
                        
                        //resize
                        Resize(asset.Texture, sheet, picture, row, column, request.assetsConfig.GetFieldKeepAspectRatio(field.Id));
                        addedCount++;
                    }
                    catch (Exception)
                    {
                        Debug.LogError(
                            $"Error while adding an image from field={field.FullName}, entity #={entity.Index}, entity name={BGEditorUtility.GetEntityName(entity)}");
                        throw;
                    }
                });
                request.logger.AppendLine($"Field {field.FullName}. {addedCount} images added");
            });
        }

        private void Resize(Texture texture, XSSFSheet sheet, IPicture picture, int rowIndex, int columnIndex, bool keepAspectRatio)
        {
            if(!keepAspectRatio) picture.Resize(1, 1);
            else
            {
                var row = sheet.GetRow(rowIndex);
                if (row != null)
                {
                    // var cell = (XSSFCell)row.GetCell(columnIndex, MissingCellPolicy.RETURN_NULL_AND_BLANK) ?? row.CreateCell(columnIndex);
                    //cell width and height use different coordinates (copy from ImageUtils.GetRowHeightInPixels)
                    var cellHeight = Units.ToEMU(row.HeightInPoints) / (float)Units.EMU_PER_PIXEL;
                    var cellWidth = sheet.GetColumnWidthInPixels(columnIndex);
                    if (cellWidth == 0)
                    {
                        //default width fix
                        sheet.SetColumnWidth(columnIndex, (int) (8 * 256));
                        cellWidth = sheet.GetColumnWidthInPixels(columnIndex);
                    }
                    var textureHeight = texture.height;
                    var textureWidth = texture.width;
                    var cellRatio = cellWidth / cellHeight;
                    var textureRatio = textureWidth / (float)textureHeight;
                    if (cellRatio > textureRatio) picture.Resize(textureRatio / cellRatio, 1);
                    else picture.Resize(1, cellRatio / textureRatio);
                }
                else picture.Resize(1, 1);
            }
        }

        //backported from NPOI 2.7 (existing code throws exceptions)
        private int AddPicture(BGExcelImageProcessorExportRequest request, byte[] pictureData, PictureType pictureType,
            Dictionary<int, POIXMLRelation> RELATIONS, MethodInfo addRelationMethod)
        {
            int imageNumber = 1;
            var workbook = (XSSFWorkbook)request.book;
            List<XSSFPictureData> allPics = (List<XSSFPictureData>)workbook.GetAllPictures();

            if (allPics.Any())
            {
                List<int> sortedIndexs = new List<int> { 0 };

                sortedIndexs.AddRange
                (
                    allPics
                        .Select(pic => GetFileNameIndex(pic, RELATIONS[(int)pic.PictureType]))
                        .OrderBy(i => i)
                        .ToList()
                );

                int previous = sortedIndexs[0];
                for (int index = 1; index < sortedIndexs.Count; index++)
                {
                    if (sortedIndexs[index] > previous + 1)
                        break;

                    previous = sortedIndexs[index];
                }

                imageNumber = previous + 1;
            }

            // XSSFPictureData img = (XSSFPictureData)workbook.CreateRelationship(RELATIONS[(int)pictureType], XSSFFactory.GetInstance(), imageNumber, true).DocumentPart;
            var part = (POIXMLDocumentPart.RelationPart)addRelationMethod.Invoke(workbook,
                new object[] { RELATIONS[(int)pictureType], XSSFFactory.GetInstance(), imageNumber, true });
            XSSFPictureData img = (XSSFPictureData)part.DocumentPart;
            try
            {
                Stream out1 = img.GetPackagePart().GetOutputStream();
                out1.Write(pictureData, 0, pictureData.Length);
                out1.Close();
            }
            catch (IOException e)
            {
                throw new POIXMLException(e);
            }

            workbook.GetAllPictures().Add(img);

            // returns image Index
            return allPics.Count - 1;
        }

        //backported from NPOI 2.7 (existing code throws exceptions)
        public int GetFileNameIndex(POIXMLDocumentPart part, POIXMLRelation rel)
        {
            Regex regex = new Regex(rel.DefaultFileName.Replace("#", "(\\d+)"));
            return int.Parse(regex.Match(part.GetPackagePart().PartName.Name).Groups[1].Value);
            //return Integer.valueOf(part.getPackagePart().getPartName().getName().replaceAll(regex, "$1"));
        }
    }
}