using FizzyLauncher.Models;
using FizzyLauncher.Text.Json;
using FizzyLauncher.Windows.Forms;
using RyzStudio.Windows.Forms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FizzyLauncher
{
    public partial class MainForm : Form
    {
        [DllImport("user32.dll")]
        protected static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

        [DllImport("user32.dll")]
        protected static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        //protected const int MOD_ALT = 0x1;
        //protected const int MOD_CONTROL = 0x2;
        //protected const int MOD_SHIFT = 0x4;
        //protected const int MOD_WIN = 0x8;
        protected const int WM_HOTKEY = 0x312;
        protected const int WM_QUERYENDSESSION = 0x0011;

        protected OptionsForm optionsForm = null;
        protected string sessionFilename = null;
        protected bool isBusy = false;
        protected bool requestExit = false;


        public MainForm() : base()
        {
            InitializeComponent();

            notifyIcon1.Text = Application.ProductName;

            this.StartPosition = FormStartPosition.WindowsDefaultBounds;
            this.Visible = false;
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            ThreadControl.SetVisible(this, false);
            ThreadControl.SetSize(this, this.MinimumSize);
        }

        protected async override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            ThreadControl.SetVisible(this, false);

            string jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig");
            if (File.Exists(jsonfigFilename))
            {
                await loadFile(jsonfigFilename);
            }
            else
            {
                this.CurrentSession = new LauncherSession();

                ThreadControl.SetVisible(this, true);
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            if (this.CurrentSession == null) this.CurrentSession = new LauncherSession();

            if (this.CurrentSession.HideOnClose && !requestExit)
            {
                this.Visible = !this.Visible;
                e.Cancel = true;
                return;
            }

            requestExit = false;

            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                // do nothing
            }
            else
            {
                if (this.CurrentSession.AutoSave == LauncherSession.AutoSaveOption.Prompt)
                {
                    DialogResult dr = MessageBox.Show("Save existing session?", "Exit", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                    if (dr == DialogResult.Yes)
                    {
                        bool rv = saveFile(sessionFilename, false);
                        if (!rv)
                        {
                            e.Cancel = true;
                        }
                    }
                    else if (dr == DialogResult.No)
                    {
                        // do nothing
                    }
                    else if (dr == DialogResult.Cancel)
                    {
                        e.Cancel = true;
                    }
                }
                else if (this.CurrentSession.AutoSave == LauncherSession.AutoSaveOption.Yes)
                {
                    saveFile(sessionFilename, false);
                }
            }

            if (this.CurrentSession.HotKey != null)
            {
                if (this.CurrentSession.HotKey.KeyCode != Keys.None)
                {
                    UnregisterHotKey((IntPtr)Handle, 1);
                }
            }

        }

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_HOTKEY:
                    if (m.WParam.ToInt32() == 1)
                    {
                        this.Visible = !this.Visible;
                    }

                    break;
                case WM_QUERYENDSESSION:
                    requestExit = true;
                    //this.Close();
                    Application.Exit();

                    break;
                default: break;

            }

            base.WndProc(ref m);
        }


        public LauncherSession CurrentSession { get; set; } = null;


        protected async Task collapseWindow(int width, int increment = 6)
        {
            await Task.Run(() =>
            {
                while (this.Width > width)
                {
                    ThreadControl.SetWidth(this, (this.Width - increment));

                    Application.DoEvents();
                }

                ThreadControl.SetWidth(this, width);
            });
        }

        protected async Task expandWindow(int width, int increment = 8)
        {
            await Task.Run(() =>
            {
                while (this.Width < width)
                {
                    ThreadControl.SetWidth(this, (this.Width + increment));

                    Application.DoEvents();
                }

                ThreadControl.SetWidth(this, width);
            });
        }

        protected void invalidateHotKey()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(() =>
                {
                    UnregisterHotKey((IntPtr)Handle, 1);
                }));
            }
            else
            {
                UnregisterHotKey((IntPtr)Handle, 1);
            }

            if (this.CurrentSession.HotKey != null)
            {
                if (this.CurrentSession.HotKey.KeyCode != Keys.None)
                {
                    if (this.InvokeRequired)
                    {
                        this.Invoke(new MethodInvoker(() =>
                        {
                            RegisterHotKey((IntPtr)Handle, 1, this.CurrentSession.HotKey.ModifierCode, this.CurrentSession.HotKey.Key);
                        }));
                    }
                    else
                    {
                        RegisterHotKey((IntPtr)Handle, 1, this.CurrentSession.HotKey.ModifierCode, this.CurrentSession.HotKey.Key);
                    }
                }
            }
        }


        protected void newSession()
        {
            flowLayoutPanel1.Controls.Clear();
            flowLayoutPanel1.Controls.Add(new TilePanelLayout(new TileGroupModel()
            {
                Title = "New Group",
                IsExpanded = true,
                GridSize = new Size(6, 1)
            }));

            InvalidateWidth(6);
        }

        protected async Task loadFile(string filename)
        {
            await Task.Run(() =>
            {
                if (isBusy)
                {
                    return;
                }

                if (string.IsNullOrWhiteSpace(filename))
                {
                    return;
                }

                if (!File.Exists(filename))
                {
                    return;
                }

                string sourceCode = null;

                try
                {
                    sessionFilename = filename;

                    sourceCode = File.ReadAllText(sessionFilename);
                }
                catch (Exception exc)
                {
                    MessageBox.Show(exc.Message, "Load session");
                    return;
                }

                if (string.IsNullOrWhiteSpace(sourceCode))
                {
                    return;
                }

                // load options
                var options = new JsonSerializerOptions();
                options.Converters.Add(new JsonPointConverter());
                options.Converters.Add(new JsonSizeConverter());

                try
                {
                    this.CurrentSession = JsonSerializer.Deserialize<LauncherSession>(sourceCode, options);
                }
                catch (Exception exc)
                {
                    MessageBox.Show("Unable to read session", "Load session");
                    return;
                }

                if (this.CurrentSession == null)
                {
                    this.CurrentSession = new LauncherSession();
                }

                // load tiles
                int maxTileWidth = 0;
                ThreadControl.Clear(flowLayoutPanel1);

                if (this.CurrentSession.Groups != null)
                {
                    foreach (TileGroupModel item in this.CurrentSession.Groups)
                    {
                        maxTileWidth = Math.Max(maxTileWidth, item.GridSize.Width);

                        TilePanelLayout panel = new TilePanelLayout(item);

                        ThreadControl.Add(flowLayoutPanel1, panel);
                    }
                }

                // resize
                InvalidateSize(maxTileWidth);

                // reposition
                if (!this.CurrentSession.StartPosition.IsEmpty) ThreadControl.SetLocation(this, this.CurrentSession.StartPosition);

                //
                ThreadControl.SetTopMost(this, this.CurrentSession.AlwaysOnTop);
                ThreadControl.SetVisible(this, true);
                ThreadControl.SetChecked(enableAnimationsToolStripMenuItem, this.CurrentSession.EnableAnimation);
                ThreadControl.SetChecked(showBigIconsToolStripMenuItem, this.CurrentSession.EnableBigIconInFolder);

                ThreadControl.SetFocus(this);

                // hotkey
                invalidateHotKey();

            });
        }

        protected bool saveFile(string filename, bool showNotices = true)
        {
            if (isBusy)
            {
                return false;
            }

            if (string.IsNullOrWhiteSpace(filename))
            {
                return false;
            }

            if (flowLayoutPanel1.Controls.Count <= 0)
            {
                return true;
            }

            isBusy = true;

            // update session
            if (this.CurrentSession == null)
            {
                this.CurrentSession = new LauncherSession();
            }

            this.CurrentSession.DefaultHeight = this.Height;
            this.CurrentSession.AlwaysOnTop = this.TopMost;
            this.CurrentSession.StartPosition = this.Location;

            // save
            this.CurrentSession.Groups = new List<TileGroupModel>();
            for (int i = 0; i < flowLayoutPanel1.Controls.Count; i++)
            {
                if (flowLayoutPanel1.Controls[i].GetType() != typeof(TilePanelLayout))
                {
                    continue;
                }

                TilePanelLayout container = flowLayoutPanel1.Controls[i] as TilePanelLayout;
                this.CurrentSession.Groups.Add(container.Model);
            }

            var options = new JsonSerializerOptions();
            options.Converters.Add(new JsonPointConverter());
            options.Converters.Add(new JsonSizeConverter());

            try
            {
                File.WriteAllText(filename, JsonSerializer.Serialize(this.CurrentSession, options));

                if (showNotices)
                {
                    MessageBox.Show("Session saved!", "Save session", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "Save session");

                return false;
            }

            isBusy = false;

            return true;
        }

        protected bool saveAsFile()
        {
            if (isBusy)
            {
                return false;
            }

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                bool rv = saveFile(saveFileDialog1.FileName);
                if (rv)
                {
                    sessionFilename = saveFileDialog1.FileName;
                }

                return rv;
            }

            return false;
        }


        protected void InvalidateSize(int columnWidth)
        {
            int newWidth = TilePanelLayout.CalcWidth(columnWidth);
            newWidth += SystemInformation.VerticalScrollBarWidth;
            newWidth += flowLayoutPanel1.Padding.Horizontal + flowLayoutPanel1.Margin.Horizontal;
            newWidth += panel1.Left + panel1.Padding.Horizontal + panel1.Margin.Horizontal;
            newWidth += this.Padding.Horizontal;

            ThreadControl.ClientSize(this, newWidth, this.CurrentSession.DefaultHeight);
        }

        protected void InvalidateWidth(int columnWidth)
        {
            int newWidth = TilePanelLayout.CalcWidth(columnWidth);
            newWidth += SystemInformation.VerticalScrollBarWidth;
            newWidth += panel1.Left + panel1.Padding.Horizontal + this.Padding.Horizontal + flowLayoutPanel1.Padding.Horizontal + flowLayoutPanel1.Left;

            ThreadControl.SetClientWidth(this, newWidth);
        }


        #region main menu

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                newSession();
            }
            else
            {
                DialogResult dr = MessageBox.Show("Save existing session?", "New session", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    bool rv = saveFile(sessionFilename, false);
                    if (rv)
                    {
                        newSession();

                        sessionFilename = null;
                    }
                }
                else if (dr == DialogResult.No)
                {
                    newSession();

                    sessionFilename = null;
                }
                else if (dr == DialogResult.Cancel)
                {
                    return;
                }
            }
        }

        private async void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    await loadFile(openFileDialog1.FileName);
                }
            }
            else
            {
                DialogResult dr = MessageBox.Show("Save existing session?", "Open session", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    bool rv = saveFile(sessionFilename, false);
                    if (rv)
                    {
                        if (openFileDialog1.ShowDialog() == DialogResult.OK)
                        {
                            await loadFile(openFileDialog1.FileName);
                        }
                    }
                }
                else if (dr == DialogResult.No)
                {
                    if (openFileDialog1.ShowDialog() == DialogResult.OK)
                    {
                        await loadFile(openFileDialog1.FileName);
                    }
                }
                else if (dr == DialogResult.Cancel)
                {
                    return;
                }
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                flowLayoutPanel1.Controls.Clear();
            }
            else
            {
                DialogResult dr = MessageBox.Show("Save existing session?", "Close session", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    bool rv = saveFile(sessionFilename, false);
                    if (rv)
                    {
                        flowLayoutPanel1.Controls.Clear();

                        sessionFilename = null;
                    }
                }
                else if (dr == DialogResult.No)
                {
                    flowLayoutPanel1.Controls.Clear();

                    sessionFilename = null;
                }
                else if (dr == DialogResult.Cancel)
                {
                    return;
                }
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                saveAsFile();
            }
            else
            {
                saveFile(sessionFilename, true);
            }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            saveAsFile();
        }

        private void exitToolStripMenuItem2_Click(object sender, EventArgs e)
        {
            requestExit = true;

            this.Close();
        }


        private void showBigIconsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.CurrentSession == null)
            {
                return;
            }

            this.CurrentSession.EnableBigIconInFolder = !this.CurrentSession.EnableBigIconInFolder;

            showBigIconsToolStripMenuItem.Checked = this.CurrentSession.EnableBigIconInFolder;
        }

        private void enableAnimationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.CurrentSession == null)
            {
                return;
            }

            this.CurrentSession.EnableAnimation = !this.CurrentSession.EnableAnimation;

            enableAnimationsToolStripMenuItem.Checked = this.CurrentSession.EnableAnimation;
        }

        private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.TopMost = !this.TopMost;
        }


        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (optionsForm == null) optionsForm = new OptionsForm(this);
            optionsForm.ShowDialog();

            invalidateHotKey();
        }


        private void viewHelpToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start("https://www.hiimray.co.uk/software-fizzy-launcher");
            }
            catch
            {
                // do nothing
            }
        }

        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(Application.ProductName + " v" + Application.ProductVersion, "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }


        private void menuStrip1_MenuActivate(object sender, EventArgs e)
        {
            alwaysOnTopToolStripMenuItem.Checked = this.TopMost;
            saveAsToolStripMenuItem.Enabled = !string.IsNullOrWhiteSpace(sessionFilename);
        }


        #endregion

        #region notification icon

        private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.Visible = !this.Visible;
            }
        }

        private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            requestExit = true;

            this.Close();
        }


        #endregion


    }
}