using System;
using System.Threading.Tasks;
using System.IO;
using Elwig.Helpers;
using System.Collections.Generic;
using System.Linq;
using Elwig.Helpers.Printing;

namespace Elwig.Documents {
    public abstract partial class Document : IDisposable {

        public static string Name => "Dokument";

        private static readonly double GenerationProportion = 0.125;

        private TempFile? _pdfFile = null;

        public bool ShowFoldMarks = App.Config.Debug;
        public bool DoubleSided = false;

        public string DataPath;
        public int CurrentNextSeason;
        public string? DocumentId;
        public string Title;
        public string Author;
        public string Header;
        public string Footer;
        public DateTime Date;

        public Document(string title) {
            var c = App.Client;
            DataPath = App.DataPath;
            CurrentNextSeason = Utils.CurrentNextSeason;
            Title = title;
            Author = c.NameFull;
            Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
            Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
                .Item(c.NameFull).NextLine()
                .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
                .Item(c.EmailAddress != null ? $"<a href=\"mailto:{c.Name} {c.NameSuffix} <{c.EmailAddress}>\">{c.EmailAddress}</a>" : null)
                    .Item(c.Website != null ? $"<a href=\"http://{c.Website}/\">{c.Website}</a>" : null)
                    .Item("Betriebs-Nr.", c.LfbisNr).Item("UID", c.UstIdNr).NextLine()
                .Item("BIC", c.Bic).Item("IBAN", c.Iban)
                .ToString();
            Date = DateTime.Today;
        }

        ~Document() {
            Dispose();
        }

        public void Dispose() {
            _pdfFile?.Dispose();
            _pdfFile = null;
            GC.SuppressFinalize(this);
        }

        public static Document Merge(IEnumerable<Document> docs) {
            return new MergedDocument(docs);
        }

        private async Task<string> Render() {
            string name;
            if (this is BusinessLetter) {
                name = "BusinessLetter";
            } else if (this is DeliveryNote) {
                name = "DeliveryNote";
            } else if (this is CreditNote) {
                name = "CreditNote";
            } else if (this is DeliveryJournal) {
                name = "DeliveryJournal";
            } else if (this is Letterhead) {
                name = "Letterhead";
            } else if (this is DeliveryConfirmation) {
                name = "DeliveryConfirmation";
            } else if (this is MemberDataSheet) {
                name = "MemberDataSheet";
            } else {
                throw new InvalidOperationException("Invalid document object");
            }
            return await Render(name);
        }

        private async Task<string> Render(string name) {
            return await Html.CompileRenderAsync(name, this); ;
        }

        public async Task Generate(IProgress<double>? progress = null) {
            progress?.Report(0.0);
            if (this is MergedDocument m) {
                var pdf = new TempFile("pdf");
                var tmpHtmls = new List<TempFile>();
                var n = m.Documents.Count();
                int i = 0;
                foreach (var doc in m.Documents) {
                    var tmpHtml = new TempFile("html");
                    await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
                    tmpHtmls.Add(tmpHtml);
                    i++;
                    progress?.Report(GenerationProportion * 100 * i / n);
                }
                progress?.Report(GenerationProportion * 100);
                await Pdf.Convert(tmpHtmls.Select(f => f.FileName), pdf.FileName, DoubleSided, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
                foreach (var tmp in tmpHtmls) {
                    tmp.Dispose();
                }
                _pdfFile = pdf;
            } else {
                var pdf = new TempFile("pdf");
                using (var tmpHtml = new TempFile("html")) {
                    await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
                    progress?.Report(50.0);
                    await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, DoubleSided);
                }
                _pdfFile = pdf;
            }
            progress?.Report(100.0);
        }

        public void SaveTo(string pdfPath) {
            if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
            File.Copy(_pdfFile.FilePath, pdfPath);
        }

        public async Task Print(int copies = 1) {
            if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
            await Pdf.Print(_pdfFile.FilePath, copies);
        }

        public void Show() {
            if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
            Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : ""));
        }

        private class MergedDocument : Document {
            public IEnumerable<Document> Documents;
            public MergedDocument(IEnumerable<Document> docs) : base("Mehrere Dokumente") {
                Documents = docs;
            }
        }
    }
}