using Hotline.Share.Dtos.ExportWord;
using Hotline.Share.Enums.ExportWord;
using Novacode;
using System.Drawing;
using XF.Domain.Exceptions;

namespace Hotline.Application.ExportWord
{
    public class WordExportProvider : IWordExportProvider
    {
        public ExportByteWord ExportFromTemplate<T>(string templatePath, T data) where T : IWordExportTemplate
        {
            DocX docX = GetDocX(templatePath);
            ReplacePlaceholders(docX, data);
            using MemoryStream memoryStream = new();
            docX.SaveAs(memoryStream);
            return new ExportByteWord
            {
                WordBytes = memoryStream.ToArray()
            };
        }

        public async Task<ExportByteWord> ExportFromTemplateAsync<T>(string templatePath, T data) where T : IWordExportTemplate
        {
            return await Task.Run(() => ExportFromTemplate(templatePath, data));
        }

        private void ReplacePlaceholders<T>(DocX word, T wordData) where T : IWordExportTemplate
        {
            if (word == null)
            {
                throw new ArgumentNullException("word");
            }

            IEnumerable<PlaceholderEntity> replacements = wordData.GetReplacements();
            if (replacements.Count() == 0)
            {
                throw UserFriendlyException.SameMessage("实体中没有可替换的属性");
            }

            // ReplacePlaceholdersInWord(word, replacements);
            if (word == null)
                throw new ArgumentNullException("word");

            if (replacements == null)
                throw new ArgumentNullException("placeholderEntities");

            foreach (PlaceholderEntity placeholderEntity in replacements)
            {
                switch (placeholderEntity.PlaceholderType)
                {
                    case EPlaceholderType.Table:
                        ReplacePlaceholdersInTable(word, placeholderEntity.Placeholder, (ExportTable)placeholderEntity.Data);
                        break;
                    case EPlaceholderType.Text:
                        ReplacePlaceholdersInText(word, placeholderEntity.Placeholder, (ExportText)placeholderEntity.Data);
                        break;
                    case EPlaceholderType.Picture:
                        ReplacePlaceholdersInImage(word, placeholderEntity.Placeholder, placeholderEntity.Pictures);
                        break;
                    case EPlaceholderType.Paragraph:
                        ReplacePlaceholdersInParagraph(word, placeholderEntity.Placeholder, (ExportParagraph)placeholderEntity.Data);
                        break;
                    case EPlaceholderType.Complex:
                        ReplacePlaceholdersInComplex(word, placeholderEntity.Placeholder, (ExportComplex)placeholderEntity.Data);
                        break;
                }
            }
        }

        private DocX GetDocX(string fileUrl)
        {
            if (!System.IO.File.Exists(fileUrl))
                throw UserFriendlyException.SameMessage("找不到模板文件");

            try
            {
                using FileStream stream = System.IO.File.OpenRead(fileUrl);
                return DocX.Load(stream);
            }
            catch (Exception ex)
            {
                throw UserFriendlyException.SameMessage("打开模板文件失败，异常原因：" + ex.Message);
            }
        }

        private void ReplacePlaceholdersInComplex(DocX word, string oldText, ExportComplex newComplex)
        {
            foreach (Paragraph paragraph2 in word.Paragraphs)
            {
                if (!paragraph2.Text.Contains(oldText))
                    continue;

                if (newComplex != null)
                {
                    newComplex.Elements.Reverse();
                    foreach (IWordElement element in newComplex.Elements)
                    {
                        if (element.GetType() == typeof(ExportParagraph))
                        {
                            ExportParagraph fWParagraph = (ExportParagraph)element;
                            Paragraph paragraph = word.InsertParagraph();
                            paragraph.Append(fWParagraph.Run.Text);
                            if (fWParagraph.Run.IsBold)
                            {
                                paragraph.Bold();
                            }

                            paragraph.FontSize(fWParagraph.Run.FontSize);
                            paragraph.Font(fWParagraph.Run.FontFamily);
                            paragraph.Color(fWParagraph.Run.Color);
                            paragraph.Alignment = fWParagraph.Alignment;
                            paragraph2.InsertParagraphAfterSelf(paragraph);
                            paragraph.Remove(trackChanges: false);
                        }
                        else if (element.GetType() == typeof(ExportTable))
                        {
                            ExportTable fWTable = (ExportTable)element;
                            Table t = FWTableToTable(word, fWTable);
                            paragraph2.InsertTableAfterSelf(t);
                        }
                        else if (element.GetType() == typeof(ExportPicture))
                        {
                            try
                            {
                                ExportPicture fWPicture = (ExportPicture)element;
                                Stream stream = ((fWPicture.PictureData != null) ? fWPicture.PictureData : System.IO.File.OpenRead(fWPicture.PictureUrl));
                                Novacode.Image image = word.AddImage(stream);
                                paragraph2.AppendPicture(image.CreatePicture(fWPicture.Height, fWPicture.Width));
                            }
                            catch
                            {
                            }
                        }
                    }
                }

                paragraph2.ReplaceText(oldText, "");
            }
        }

        private void ReplacePlaceholdersInParagraph(DocX word, string oldText, ExportParagraph newParagraph)
        {
            foreach (Paragraph paragraph2 in word.Paragraphs)
            {
                if (paragraph2.Text.Contains(oldText))
                {
                    Paragraph paragraph = word.InsertParagraph();
                    paragraph.Append(newParagraph.Run.Text);
                    if (newParagraph.Run.IsBold)
                        paragraph.Bold();

                    paragraph.FontSize(newParagraph.Run.FontSize);
                    paragraph.Font(newParagraph.Run.FontFamily);
                    paragraph.Color(newParagraph.Run.Color);
                    paragraph.Alignment = newParagraph.Alignment;
                    paragraph2.InsertParagraphAfterSelf(paragraph);
                    paragraph2.Remove(trackChanges: false);
                }
            }
        }

        private void ReplacePlaceholdersInText(DocX word, string oldText, ExportText newText)
        {
            foreach (Paragraph paragraph in word.Paragraphs)
            {
                if (paragraph.Text.Contains(oldText))
                    paragraph.ReplaceText(oldText, newText.Data);
            }
        }

        private void ReplacePlaceholdersInTable(DocX word, string oldText, ExportTable newTable)
        {
            foreach (Paragraph paragraph in word.Paragraphs)
            {
                if (paragraph.Text.Contains(oldText))
                {
                    if (newTable != null)
                    {
                        Table t = FWTableToTable(word, newTable);
                        paragraph.InsertTableAfterSelf(t);
                        paragraph.Remove(trackChanges: false);
                    }
                    else
                        paragraph.ReplaceText(oldText, "");
                }
            }
        }

        private void ReplacePlaceholdersInImage(DocX word, string oldText, IEnumerable<ExportPicture> newPic)
        {
            IEnumerable<ExportPicture> enumerable;
            if (newPic != null)
                enumerable = newPic;
            else
            {
                IEnumerable<ExportPicture> enumerable2 = new List<ExportPicture>();
                enumerable = enumerable2;
            }

            newPic = enumerable;
            foreach (Paragraph paragraph2 in word.Paragraphs)
            {
                Paragraph paragraph = paragraph2;
                if (!paragraph.Text.Contains(oldText))
                    continue;

                if (newPic.Count() > 0)
                {
                    newPic.ToList().ForEach(delegate (ExportPicture pic)
                    {
                        try
                        {
                            paragraph.AppendPicture(word.AddImage(pic.PictureData ?? System.IO.File.OpenRead(pic.PictureUrl)).CreatePicture(pic.Height, pic.Width));
                        }
                        catch
                        {
                        }
                    });
                }

                paragraph.ReplaceText(oldText, "");
            }
        }

        private Table FWTableToTable(DocX word, ExportTable FWTable)
        {
            Table table = word.AddTable(FWTable.RowCount, FWTable.ColumnCount);
            table.Alignment = Alignment.center;
            table.SetBorder(TableBorderType.InsideH, new Border());
            table.SetBorder(TableBorderType.Top, new Border());
            table.SetBorder(TableBorderType.Bottom, new Border());
            table.SetBorder(TableBorderType.Left, new Border());
            table.SetBorder(TableBorderType.Right, new Border());
            table.SetBorder(TableBorderType.InsideV, new Border());
            for (int i = 0; i < FWTable.Rows.Count; i++)
            {
                if (FWTable.Rows[i].Height > 0.0)
                    table.Rows[i].Height = FWTable.Rows[i].Height;

                for (int j = 0; j < FWTable.Rows[i].Cells.Count; j++)
                {
                    ExportTableCell fWTableCell = FWTable.Rows[i].Cells[j];
                    if (table.Rows[i].Cells[j].Width > 0.0)
                        table.Rows[i].Cells[j].Width = fWTableCell.Width;

                    if (fWTableCell.FillColor != Color.Empty)
                        table.Rows[i].Cells[j].FillColor = fWTableCell.FillColor;

                    table.Rows[i].Cells[j].VerticalAlignment = fWTableCell.VerticalAlignment;
                    table.Rows[i].Cells[j].RemoveParagraphAt(0);
                    foreach (ExportParagraph paragraph in fWTableCell.Paragraphs)
                    {
                        Paragraph p = table.Rows[i].Cells[j].InsertParagraph();
                        if (!string.IsNullOrEmpty(paragraph.Run.Text))
                            p.Append(paragraph.Run.Text);

                        if (paragraph.Run.IsBold)
                            p.Bold();

                        p.FontSize(paragraph.Run.FontSize);
                        p.Font(paragraph.Run.FontFamily);
                        p.Color(paragraph.Run.Color);
                        p.Alignment = paragraph.Alignment;
                        if (paragraph.Run.Pictures.Count > 0)
                        {
                            paragraph.Run.Pictures.ForEach(delegate (ExportPicture t)
                            {
                                p.AppendPicture(word.AddImage(t.PictureData ?? System.IO.File.OpenRead(t.PictureUrl)).CreatePicture(t.Height, t.Width));
                            });
                        }
                    }
                }
            }

            foreach (var mergeCellsInColumn in FWTable.MergeCellsInColumns)
            {
                table.MergeCellsInColumn(mergeCellsInColumn.Item1, mergeCellsInColumn.Item2, mergeCellsInColumn.Item3);
            }

            foreach (var mergeCellsInRow in FWTable.MergeCellsInRows)
            {
                table.Rows[mergeCellsInRow.Item1].MergeCells(mergeCellsInRow.Item2, mergeCellsInRow.Item3);
                if (!(table.Rows[mergeCellsInRow.Item1].Cells[mergeCellsInRow.Item2].Paragraphs[0].Text == "备注"))
                {
                }

                while (table.Rows[mergeCellsInRow.Item1].Cells[mergeCellsInRow.Item2].Paragraphs.Count > 1)
                {
                    table.Rows[mergeCellsInRow.Item1].Cells[mergeCellsInRow.Item2].RemoveParagraphAt(1);
                }
            }

            return table;
        }
    }
}
