using Kbg.NppPluginNET.PluginInfrastructure;
using MarkdownRenderer;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace NppMarkdownRenderer
{
    internal static class PluginBridge
    {
        private static readonly IScintillaGateway CurrentEditor = new ScintillaGateway(PluginBase.GetCurrentScintilla());
        private static readonly MarkdownRendererForm RendererForm = new MarkdownRendererForm();

        // for get current editing file info
        private static readonly INotepadPPGateway Notepad = new NotepadPPGateway();

        public static bool Enable { get; private set; }

        public static void SetupPluginMenu()
        {
            PluginBase.SetCommand(1, "Toggle Markdown Renderer", ShowMarkdownRenderer);
        }

        public static void SetupToolBarIcon()
        {
            var toolbarIcons = new toolbarIcons { hToolbarBmp = MarkdownRendererResources.MainIconBm16.GetHbitmap() };
            var pTbIcons = Marshal.AllocHGlobal(Marshal.SizeOf(toolbarIcons));
            Marshal.StructureToPtr(toolbarIcons, pTbIcons, false);
            Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_ADDTOOLBARICON, PluginBase._funcItems.Items[0]._cmdID, pTbIcons);
            Marshal.FreeHGlobal(pTbIcons);
        }

        private static void ShowMarkdownRenderer()
        {
            if (false == RendererForm.Created)
            {
                RendererForm.VisibleChanged += delegate
                {
                    if (RendererForm.Visible)
                    {
                        FlushState();
                        UpdateText();
                        UpdateScroll();
                        Enable = true;
                        Task.Factory.StartNew(() =>
                        {
                            Thread.Sleep(100);
                            CurrentEditor.SetFocus(true);
                        });
                    }
                    else
                    {
                        Enable = false;
                        Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_SETMENUITEMCHECK, PluginBase._funcItems.Items[0]._cmdID, 0);
                    }
                };

                RendererForm.SizeChanged += delegate
                {
                    CurrentEditor.SetFocus(true);
                };

                // dlgID: the dlgDlg should be the index of funcItem where the current function pointer is in
                //     this case is 15.. so the initial value of funcItem[15]._cmdID - not the updated internal one !
                // uMask: define the default docking behaviour

                var nppTbData = new NppTbData
                {
                    pszModuleName = AnyCpuBridgeMain.PluginName,
                    hClient = RendererForm.Handle,
                    pszName = RendererForm.Text,

                    hIconTab = (uint)MarkdownRendererResources.MainIcon.Handle,
                    uMask = NppTbMsg.DWS_DF_CONT_LEFT | NppTbMsg.DWS_ICONTAB | NppTbMsg.DWS_ICONBAR,
                    dlgID = 1,
                };

                var ptrNppTbData = Marshal.AllocHGlobal(Marshal.SizeOf(nppTbData));
                Marshal.StructureToPtr(nppTbData, ptrNppTbData, false);
                Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_DMMREGASDCKDLG, IntPtr.Zero, ptrNppTbData);
                Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_SETMENUITEMCHECK, PluginBase._funcItems.Items[0]._cmdID, 1);

                Enable = true;
            }
            else
            {
                if (!RendererForm.Visible)
                {
                    Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_DMMSHOW, 0, RendererForm.Handle);
                    Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_SETMENUITEMCHECK, PluginBase._funcItems.Items[0]._cmdID, 1);
                    Enable = true;
                }
                else
                {
                    Enable = false;
                    Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_DMMHIDE, 0, RendererForm.Handle);
                    Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_SETMENUITEMCHECK, PluginBase._funcItems.Items[0]._cmdID, 0);
                }
            }
        }

        public static void FlushState()
        {
            CurrentEditor.SetScintillaHandle(PluginBase.GetCurrentScintilla());
        }

        public static void UpdateText()
        {
            var filePath = Notepad.GetCurrentFilePath();
            var mdText = true == filePath?.EndsWith(".md", StringComparison.InvariantCultureIgnoreCase) ? CurrentEditor.GetText(CurrentEditor.GetLength() + 1) : "ERROR: Only Markdown(*.md) supported";
            RendererForm.PutMarkdown(mdText);
        }

        public static void UpdateScroll()
        {
            var scrollInfo = CurrentEditor.GetScrollInfo(Win32.ScrollInfoMask.SIF_RANGE | Win32.ScrollInfoMask.SIF_TRACKPOS | Win32.ScrollInfoMask.SIF_PAGE, Win32.ScrollInfoBar.SB_VERT);
            double totalRange = scrollInfo.nMax - scrollInfo.nMin + 1;
            double scrollRatio;

            // Is "Enable scrolling beyond last line" checked?
            if (CurrentEditor.GetEndAtLastLine() == false)
            {
                var actualThumbHeight = totalRange / (totalRange / scrollInfo.nPage - 1);
                var actualTrackPos = scrollInfo.nTrackPos * actualThumbHeight / scrollInfo.nPage;

                scrollRatio = Math.Min(1, actualTrackPos / (totalRange - actualThumbHeight));
            }
            else
            {
                scrollRatio = scrollInfo.nTrackPos / (totalRange - scrollInfo.nPage);
            }

            RendererForm.ScrollToPercent(scrollRatio);
        }

        public static void Exit()
        {
            RendererForm.Close();
            RendererForm.Dispose();
            MarkdownRendererMain.Quit();
        }
    }
}