using AngleSharp;
using AngleSharp.Css.Dom;
using AngleSharp.Dom;
using AngleSharp.Html.Parser;
using CefSharpWrap;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;

namespace MarkdownRenderer
{
    public partial class MarkdownRendererForm : Form
    {
        private readonly CefSharpWrapBrowser _rendererBrowser;
        private readonly HtmlParser _agHtmlParser;

        private string _html;
        private double _scrollPosition;
        private string _styleSheet;
        private string _bodyClassName;

        public MarkdownRendererForm()
        {
            InitializeComponent();

            _rendererBrowser = CefSharpWrapFactory.CreateSealedInstance($"{CefSharpWrapFactory.SchemeName}://{CefSharpWrapFactory.CustomResourceDomainName}/{MarkdownRendererMain.CustomResourcePath}/");
            _rendererBrowser.FillToControl(MainPanel);
            _rendererBrowser.RegisterJsObject("BridgeObject", new RenderJsObjectBridge(this));

            _rendererBrowser.ExternalRequested += delegate (object sender, string fileName)
             {
                 try
                 {
                     Process.Start(fileName);
                 }
                 catch (Exception e)
                 {
                     MessageBox.Show(e.ToString(), "External execute fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 }
             };

            var agContext = BrowsingContext.New(Configuration.Default.WithDefaultLoader().WithCss());
            _agHtmlParser = new HtmlParser(new HtmlParserOptions(), agContext);

            FillStyleSheetDropDownMenuItems(BuiltInStyleSheetsBulidinToolStripMenuItem, StyleSheetStore.BuiltInEntries);
            RefreshCustomStyleSheetMenuItems();

            //TODO: Active last used style sheet
            SetStyleSheet(StyleSheetStore.BuiltInEntries[0]);
        }

        private void SetStyleSheet(StyleSheetEntry styleSheet)
        {
            _styleSheet = styleSheet.Css;
            _bodyClassName = styleSheet.BodyClass;
            _rendererBrowser.ExecuteJsAsync("syncStyle()");
        }

        public void PutMarkdown(string mdText)
        {
            var action = new Action(() =>
            {
                _html = MarkdownHtmlConverter.ToHtml(mdText);
                _rendererBrowser.ExecuteJsAsync("syncHtml()");
            });

            if (InvokeRequired) Invoke(action);
            else action();

        }

        public void ScrollToPercent(double scrollPosition)
        {
            //TODO: Sync scroll position match to section. too hard to implement
            _scrollPosition = scrollPosition;
            _rendererBrowser.ExecuteJsAsync("syncScroll()");
        }

        private void RefreshCustomStyleSheetMenuItems()
        {
            for (var i = CustomStyleSheetNoneToolStripMenuItem.DropDownItems.Count - 1; i >= 0; i--)
            {
                var item = CustomStyleSheetNoneToolStripMenuItem.DropDownItems[i];

                if (item != CustomStyleSheetNoneToolStripMenuItem) CustomStyleSheetNoneToolStripMenuItem.DropDownItems.Remove(item);
            }

            var entries = StyleSheetStore.GetEntries();

            if (false == (CustomStyleSheetNoneToolStripMenuItem.Visible = entries.Length == 0))
            {
                FillStyleSheetDropDownMenuItems(BuiltInStyleSheetsBulidinToolStripMenuItem, entries);
            }
        }

        private static void FillStyleSheetDropDownMenuItems(ToolStripDropDownItem container, IEnumerable<StyleSheetEntry> entries)
        {
            foreach (var entry in entries)
            {
                var item = new ToolStripMenuItem
                {
                    Text = entry.Name,
                    Tag = entry
                };
                //TODO: restore check state
                container.DropDownItems.Add(item);
            }
        }

        private string GenerateHtmlForExport()
        {
            var htmlTemplate = Properties.Resources.TemplatePageForExport;
            var doc = _agHtmlParser.ParseDocument(htmlTemplate);
            doc.QuerySelector("style").InnerHtml = _styleSheet;
            var body = doc.QuerySelector("body");
            body.InnerHtml = _html;
            body.ClassName = _bodyClassName;

            using (var writer = new StringWriter())
            {
                doc.ToHtml(writer);
                return writer.ToString();
            }
        }

        private void CopyToolStripButton_Click(object sender, EventArgs e)
        {
            try
            {
                var html = GenerateHtmlForExport();
                Clipboard.SetText(html);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.ToString(), "Copy HTML fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void CopyInlineStyleHtml_Click(object sender, EventArgs e)
        {
            try
            {
                var html = GenerateHtmlForExport();

                var doc = _agHtmlParser.ParseDocument(html);

                //TODO: check why pre and quote missing background-image
                foreach (var element in doc.QuerySelectorAll("body,body *"))
                {
                    var style = doc.DefaultView.GetComputedStyle(element);
                    element.SetStyle(style.CssText.Replace('\"', '\''));
                    element.RemoveAttribute("class");
                }

                foreach (var element in doc.QuerySelectorAll("style"))
                {
                    element.Remove();
                }

                var writer = new StringWriter();
                doc.ToHtml(writer);
                var processedHtml = writer.ToString();
                Clipboard.SetText(processedHtml);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.ToString(), "Copy HTML fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void SaveToolStripButton_Click(object sender, EventArgs e)
        {
            var sfd = new SaveFileDialog
            {
                Title = "Save as HTML",
                Filter = "HTML files (*.html;*.htm)|*.htm;*.html"
            };

            if (DialogResult.OK != sfd.ShowDialog()) return;

            try
            {
                var html = GenerateHtmlForExport();
                File.WriteAllText(sfd.FileName, html);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.ToString(), "Save as HTML fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void PrintToolStripButton_Click(object sender, EventArgs e)
        {
            new PrintToPdfForm(_rendererBrowser).ShowDialog();
        }

        private void StyleSheetManageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new StyleSheetForm().ShowDialog();
            //TODO: if current active entry was delete (not found in custom and bulit-in), choose an other one or reset to first built-in
            RefreshCustomStyleSheetMenuItems();
        }

        private void HelpToolStripButton_Click(object sender, EventArgs e)
        {
            new AboutForm().ShowDialog();
        }

        private void F12ToolStripButton_Click(object sender, EventArgs e)
        {
            _rendererBrowser.ShowF12();
        }

        private void MarkdownRendererForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            _rendererBrowser.Dispose();
        }

        private void StyleSheetsToolStripMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            foreach (ToolStripMenuItem item in CustomStyleSheetNoneToolStripMenuItem.DropDownItems) item.Checked = false;
            foreach (ToolStripMenuItem item in BuiltInStyleSheetsBulidinToolStripMenuItem.DropDownItems) item.Checked = false;

            var clickedItem = (ToolStripMenuItem)e.ClickedItem;
            clickedItem.Checked = true;

            SetStyleSheet((StyleSheetEntry)clickedItem.Tag);

            //TODO: Save currelt selected style sheet id to config store
        }

        private class RenderJsObjectBridge
        {
            private readonly MarkdownRendererForm _formInstance;

            public RenderJsObjectBridge(MarkdownRendererForm formInstance) => _formInstance = formInstance;

            // ReSharper disable UnusedMember.Local

            public string StyleSheet => _formInstance._styleSheet;
            public string BodyClassName => _formInstance._bodyClassName;
            public string Html => _formInstance._html;
            public double ScrollPosition => _formInstance._scrollPosition;

            // ReSharper restore UnusedMember.Local
        }
    }
}