using FizzyLauncher.Models;
using FizzyLauncher.Text.Json;
using RyzStudio.Windows.Forms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
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();

            tileContainer1.OnColumnSizeChanged += tileContainer1_OnSizeChanged;
            notifyIcon1.Text = Application.ProductName;

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

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

            this.AutoScaleMode = AutoScaleMode.None;

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

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

            ThreadControl.SetVisible(this, false);

            string[] commandLineArgs = Environment.GetCommandLineArgs();

            string jsonfigFilename = null;
            if (string.IsNullOrWhiteSpace(jsonfigFilename)) jsonfigFilename = parseOpenFile_FromCMD(commandLineArgs);
            if (string.IsNullOrWhiteSpace(jsonfigFilename)) jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig");

            if (!string.IsNullOrWhiteSpace(jsonfigFilename) && 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)
                {
#if !DEBUG
                    UnregisterHotKey((IntPtr)Handle, 1);
#endif
                }
            }

        }

        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;


        public void Clear(int columnCount)
        {
            tileContainer1.Clear();
            tileContainer1.Add(columnCount);

            sessionFilename = 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 !DEBUG
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(() =>
                {
                    UnregisterHotKey((IntPtr)Handle, 1);
                }));
            }
            else
            {
                UnregisterHotKey((IntPtr)Handle, 1);
            }
#endif

            if (this.CurrentSession.HotKey != null)
            {
                if (this.CurrentSession.HotKey.KeyCode != Keys.None)
                {
#if !DEBUG
                    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);
                    }
#endif
                }
            }
        }

        protected string parseOpenFile_FromCMD(string[] args)
        {
            if (args.Length <= 1)
            {
                return null;
            }

            int i = 1;
            while (true)
            {
                if (i > (args.Length - 1))
                {
                    break;
                }

                switch (args[i].Trim().ToLower())
                {
                    case "-o":
                    case "-open":
                        if ((i + 1) > (args.Length - 1)) break;

                        string openFilename = args[(i + 1)];
                        if (string.IsNullOrWhiteSpace(openFilename)) break;
                        if (!File.Exists(openFilename)) break;

                        return openFilename;

                        i++;
                        break;
                }

                i++;
            }

            return null;
        }

        protected void newSession()
        {
            NewForm form = new NewForm(this);
            form.ShowDialog();
        }

        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
                tileContainer1.Load(this.CurrentSession.Groups);

                // 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.SetClientHeight(this, this.CurrentSession.DefaultHeight);

                ThreadControl.SetFocus(this);

                // hotkey
                invalidateHotKey();

            });
        }

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

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

            if (tileContainer1.GroupCount <= 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;
            this.CurrentSession.Groups = tileContainer1.GroupModels?.ToList() ?? new List<TileGroupModel>();

            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;
        }

#region main menu

        /// <summary>
        /// New
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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();
                    }
                }
                else if (dr == DialogResult.No)
                {
                    newSession();
                }
                else if (dr == DialogResult.Cancel)
                {
                    return;
                }
            }
        }

        /// <summary>
        /// Open
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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;
                }
            }
        }

        /// <summary>
        /// Close
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                tileContainer1.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)
                    {
                        tileContainer1.Clear();

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

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

        /// <summary>
        /// Save
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(sessionFilename))
            {
                saveAsFile();
            }
            else
            {
                saveFile(sessionFilename, true);
            }
        }

        /// <summary>
        /// Save As
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            saveAsFile();
        }

        /// <summary>
        /// Exit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitToolStripMenuItem2_Click(object sender, EventArgs e)
        {
            requestExit = true;

            this.Close();
        }


        /// <summary>
        /// Add group
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void addGroupToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tileContainer1.Add();
        }


        /// <summary>
        /// Show big icons
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showBigIconsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.CurrentSession == null)
            {
                return;
            }

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

            showBigIconsToolStripMenuItem.Checked = this.CurrentSession.EnableBigIconInFolder;
        }

        /// <summary>
        /// Enable animations
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void enableAnimationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.CurrentSession == null)
            {
                return;
            }

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

            enableAnimationsToolStripMenuItem.Checked = this.CurrentSession.EnableAnimation;
        }

        /// <summary>
        /// Always on top
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.TopMost = !this.TopMost;
        }


        /// <summary>
        /// Options
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (optionsForm == null) optionsForm = new OptionsForm(this);
            optionsForm.ShowDialog();

            invalidateHotKey();
        }


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

        /// <summary>
        /// About
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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

        private void tileContainer1_OnSizeChanged(object sender, EventArgs e)
        {
            int newWidth = this.Padding.Horizontal + SystemInformation.VerticalScrollBarWidth + tileContainer1.CalcWidth;

            ThreadControl.SetClientWidth(this, newWidth);
        }

    }
}