using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using FizzyLauncher.Models;
using RyzStudio.Windows.Forms;
using RyzStudio.Windows.ThemedForms.ButtonTextBox;
using RyzStudio.Windows.TileForms;

namespace FizzyLauncher
{
    public partial class MainForm : Form
    {
        private readonly FileSessionManager _fileSessionManager;

        private bool _isBusy = false;
        private bool _requestExit = false;


        public MainForm()
        {
            InitializeComponent();

            _fileSessionManager = new FileSessionManager();
            _fileSessionManager.OpenFileDialog = openFileDialog1;
            _fileSessionManager.SaveFileDialog = saveFileDialog1;
            _fileSessionManager.OnNewing += fileSessionManager_OnNewSession;
            _fileSessionManager.OnLoading += fileSessionManager_OnLoadSession;
            _fileSessionManager.OnSaving += fileSessionManager_OnSaveSession;
            _fileSessionManager.OnClearing += fileSessionManager_OnClearSession;
            _fileSessionManager.OnFilenameChanged += fileSessionManager_OnFilenameChanged;

            //tileContainer1.OnColumnSizeChanged += tileContainer1_OnSizeChanged;
            notifyIcon1.Text = System.Windows.Forms.Application.ProductName;

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

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

            var args = RyzStudio.Windows.Forms.Application.GetCommandLine();

            string jsonfigFilename = args.Where(x => (x.Key.Equals("o") || x.Key.Equals("open"))).Select(x => x.Value).FirstOrDefault();
            if (string.IsNullOrWhiteSpace(jsonfigFilename))
            {
                jsonfigFilename = Path.ChangeExtension(System.Windows.Forms.Application.ExecutablePath, "jsonfig");
            }

            if (!string.IsNullOrWhiteSpace(jsonfigFilename) && File.Exists(jsonfigFilename))
            {
                await _fileSessionManager.OpenSession(jsonfigFilename);
            }
            else
            {
                this.CurrentSession = new AppOptions();

                InvalidateOptions();

                //UIControl.SetVisible(this, true);
            }
        }

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

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

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

            _requestExit = false;

            await _fileSessionManager.CloseSession();

            if ((this.CurrentSession?.ShowToggleHotkey ?? new ThKeyCodeTextBox.Results()).Key != Keys.None)
            {
#if !DEBUG
                RyzStudio.Runtime.InteropServices.User32.UnregisterHotKey((IntPtr)Handle, 1);
#endif
            }
        }

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

                    break;
                case RyzStudio.Runtime.InteropServices.User32.WM_QUERYENDSESSION:
                    _requestExit = true;

                    System.Windows.Forms.Application.Exit();

                    break;
                default:
                    break;
            }

            base.WndProc(ref m);
        }


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


        private void InvalidateOptions()
        {
            /// todo: big icons

#if !DEBUG
            UIControl.Invoke(this, (x) =>
            {
                RyzStudio.Runtime.InteropServices.User32.UnregisterHotKey((IntPtr)Handle, 1);
            });

            if ((this.CurrentSession?.ShowToggleHotkey ?? new ThKeyCodeTextBox.Results()).Key != Keys.None)
            {
                UIControl.Invoke(this, (x) =>
                {
                    RyzStudio.Runtime.InteropServices.User32.RegisterHotKey((IntPtr)Handle, 1, this.CurrentSession.HotKey.ModifierCode, this.CurrentSession.HotKey.Key);
                });
            }
#endif

            UIControl.SetTopMost(this, this.CurrentSession.AlwaysOnTop);
        }

        private void menuStrip1_MenuActivate(object sender, EventArgs e)
        {
            closeToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close);

            saveToolStripMenuItem.Enabled = (_fileSessionManager.SessionState == FileSessionManager.SessionStateEnum.Open);
            saveAsToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close);

            addGroupToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close);

            showBigIconsToolStripMenuItem.Checked = this.CurrentSession?.ShowBigIcons ?? true;
            alwaysOnTopToolStripMenuItem.Checked = this.CurrentSession?.AlwaysOnTop ?? false;
        }


        #region Main Menu

        /// <summary>
        /// New
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_isBusy)
            {
                return;
            }

            await _fileSessionManager.NewSession();
        }

        /// <summary>
        /// Open
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_isBusy)
            {
                return;
            }

            await _fileSessionManager.OpenSession();
        }

        /// <summary>
        /// Close
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_isBusy)
            {
                return;
            }

            await _fileSessionManager.CloseSession();
        }

        /// <summary>
        /// Save
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_isBusy)
            {
                return;
            }

            await _fileSessionManager.SaveSession();
        }

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

            await _fileSessionManager.SaveAsSession();
        }

        /// <summary>
        /// Exit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitToolStripMenuItem2_Click(object sender, EventArgs e)
        {
            if (_isBusy)
            {
                return;
            }

            _requestExit = true;

            this.Close();
        }


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


        /// <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.ShowBigIcons = !this.CurrentSession.ShowBigIcons;
        }

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

            this.CurrentSession.AlwaysOnTop = !this.CurrentSession.AlwaysOnTop;

            this.TopMost = this.CurrentSession.AlwaysOnTop;
        }


        /// <summary>
        /// Options
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var form = new OptionsForm(this.CurrentSession);
            if (form.ShowDialog() == DialogResult.OK)
            {
                this.CurrentSession = form.Result;

                InvalidateOptions();
            }
        }


        /// <summary>
        /// View help
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void viewHelpToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            RyzStudio.Diagnostics.Process.Execute(AppResource.AppHelpURL);
        }

        /// <summary>
        /// About
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(System.Windows.Forms.Application.ProductName + " v" + System.Windows.Forms.Application.ProductVersion, "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        #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


        #region File Session Manager

        private async Task<bool> fileSessionManager_OnNewSession(FileSessionManager sender)
        {
            return await Task.Run(() =>
            {
                var form = new NewForm();
                if (form.ShowDialog() == DialogResult.OK)
                {
                    var result = form.Result;

                    UIControl.Clear(flowLayoutPanel1);

                    this.CurrentSession.TilesPerRow = result.ColumnCount;

                    for (var i = 0; i < result.GroupCount; i++)
                    {
                        AddNewTileGroup();
                    }
                }

                AutoResize();

                return true;
            });
        }

        private async Task<bool> fileSessionManager_OnLoadSession(FileSessionManager sender, string filename)
        {
            return await Task.Run(async () =>
            {
                this.CurrentSession = RyzStudio.Text.Json.JsonSerialiser.DeserialiseFile<AppOptions>(filename);
                if (this.CurrentSession == null)
                {
                    MessageBox.Show("Unable to read session", "Load session");

                    return false;
                }

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

                // Reposition + resize
                if (!this.CurrentSession.StartPosition.IsEmpty)
                {
                    UIControl.SetLocation(this, this.CurrentSession.StartPosition);
                }

                if (this.CurrentSession.Height > 0)
                {
                    UIControl.SetClientHeight(this, this.CurrentSession.Height);
                }

                InvalidateOptions();

                // Load tiles
                await LoadTileGroups(this.CurrentSession.Groups);

                AutoResize();

                //UIControl.SetVisible(this, true);
                UIControl.SetFocus(this);

                return true;
            });
        }

        private async Task<bool> fileSessionManager_OnSaveSession(FileSessionManager sender, string filename, bool showNotices)
        {
            if (string.IsNullOrWhiteSpace(filename))
            {
                return false;
            }

            if (!flowLayoutPanel1.Controls.OfType<TileContainer>().Any())
            {
                return true;
            }

            return await Task.Run(() =>
            {
                if (_isBusy)
                {
                    return false;
                }

                _isBusy = true;

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

                this.CurrentSession.StartPosition = this.Location;
                this.CurrentSession.Height = this.Height;

                this.CurrentSession.Groups = new List<TileGroupModel>();
                foreach (var container in flowLayoutPanel1.Controls.OfType<RyzStudio.Windows.TileForms.TileContainer>())
                {
                    this.CurrentSession.Groups.Add((TileGroupModel)container.Tag);
                }

                var result = RyzStudio.Text.Json.JsonSerialiser.SerialiseFile(filename, this.CurrentSession);
                if (result.IsSuccess)
                {
                    if (showNotices)
                    {
                        MessageBox.Show("Session saved!", "Save session", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
                else
                {
                    MessageBox.Show(result.Message, "Save session");

                    _isBusy = false;

                    return false;
                }

                _isBusy = false;

                return true;
            });
        }

        private async Task<bool> fileSessionManager_OnClearSession(FileSessionManager sender)
        {
            await Task.Run(() =>
            {
                UIControl.Clear(flowLayoutPanel1);
            });

            return true;
        }

        private async Task fileSessionManager_OnFilenameChanged(FileSessionManager sender, string filename)
        {
            await Task.Run(() =>
            {
                switch (sender.SessionState)
                {
                    case FileSessionManager.SessionStateEnum.New:
                        UIControl.SetText(this, "New Session - " + System.Windows.Forms.Application.ProductName);
                        break;
                    case FileSessionManager.SessionStateEnum.Open:
                        UIControl.SetText(this, Path.GetFileNameWithoutExtension(filename) + " - " + System.Windows.Forms.Application.ProductName);
                        break;
                    case FileSessionManager.SessionStateEnum.Close:
                        UIControl.SetText(this, System.Windows.Forms.Application.ProductName);
                        break;
                    default:
                        break;
                }
            });
        }

        #endregion

        #region Tile Container

        /// <summary>
        /// Add Tile
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void addGroupToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripMenuItem)sender);
            if (container == null)
            {
                return;
            }

            var form = new EditTileForm();
            if (form.ShowDialog() == DialogResult.OK)
            {
                var result = form.Result;
                var newCoord = container.GetNextCoord();

                var newTile = new FizzyLauncher.Windows.Forms.TilePanel();
                newTile.LoadInfo(result);

                container.Add(newTile, newCoord.X, newCoord.Y);

                _fileSessionManager.HasChanged = true;
            };
        }

        /// <summary>
        /// Add Tile Group
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripMenuItem4_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripMenuItem)sender);
            if (container == null)
            {
                return;
            }

            var form = new EditTileFolderForm();
            if (form.ShowDialog() == DialogResult.OK)
            {
                var result = form.Result;
                var newCoord = container.GetNextCoord();

                var newTile = new FizzyLauncher.Windows.Forms.TilePanel();
                newTile.LoadInfo(result);

                container.Add(newTile, newCoord.X, newCoord.Y);

                _fileSessionManager.HasChanged = true;
            };
        }

        /// <summary>
        /// Edit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void editToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripMenuItem)sender);
            if (container == null)
            {
                return;
            }

            var model = UIControl.GetTag<TileGroupModel>(container);

            var form = new EditGroupForm(model);
            if (form.ShowDialog() == DialogResult.OK)
            {
                var result = form.Result;

                container.Title = result.Title;
                container.Tag = result;

                container.Invalidate();

                _fileSessionManager.HasChanged = true;
            };
        }

        /// <summary>
        /// Row - Add Row
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void addRowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripDropDownItem)sender);
            if (container == null)
            {
                return;
            }

            container.AddRow();

            _fileSessionManager.HasChanged = true;
        }

        /// <summary>
        /// Row - Remove Row
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void removeRowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripDropDownItem)sender);
            if (container == null)
            {
                return;
            }

            container.RemoveRow();

            _fileSessionManager.HasChanged = true;
        }

        /// <summary>
        /// Move Top
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void topToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripDropDownItem)sender);
            if (container == null)
            {
                return;
            }

            UIControl.MoveTop(flowLayoutPanel1, container);

            _fileSessionManager.HasChanged = true;
        }

        /// <summary>
        /// Move Up
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void upToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripDropDownItem)sender);
            if (container == null)
            {
                return;
            }

            UIControl.MoveUp(flowLayoutPanel1, container);

            _fileSessionManager.HasChanged = true;
        }

        /// <summary>
        /// Move Down
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripDropDownItem)sender);
            if (container == null)
            {
                return;
            }

            UIControl.MoveDown(flowLayoutPanel1, container);

            _fileSessionManager.HasChanged = true;
        }

        /// <summary>
        /// Move Bottom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bottomToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripDropDownItem)sender);
            if (container == null)
            {
                return;
            }

            UIControl.MoveBottom(flowLayoutPanel1, container);

            _fileSessionManager.HasChanged = true;
        }

        /// <summary>
        /// Remove
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void removeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var container = UIControl.GetOwner<RyzStudio.Windows.TileForms.TileContainer>((ToolStripMenuItem)sender);
            if (container == null)
            {
                return;
            }

            flowLayoutPanel1.Controls.Remove(container);

            _fileSessionManager.HasChanged = true;
        }

        #endregion


        private void AddNewTileGroup()
        {
            var group = new TileGroupModel();
            group.Title = "New Group";
            group.IsExpanded = true;
            group.GridSize = new System.Drawing.Size(this.CurrentSession.TilesPerRow, 1);

            var container = new RyzStudio.Windows.TileForms.TileContainer();
            container.Title = group.Title;
            container.IsOpen = group.IsExpanded;
            container.TitleContextMenuStrip = tileContainerMenu1;
            container.AutoSizeHeight = true;
            container.Tag = group;
            container.BackColor = Color.Orange;

            UIControl.Add(flowLayoutPanel1, container);

            container.AutoSize(group.GridSize.Width, group.GridSize.Height);

            _fileSessionManager.HasChanged = true;
        }

        private void AutoResize()
        {
            if (flowLayoutPanel1.Controls.Count <= 0)
            {
                return;
            }

            var width = 0;
            foreach (var item in flowLayoutPanel1.Controls.OfType<TileContainer>())
            {
                width = Math.Max(width, item.Width);
            }

            UIControl.SetClientWidth(this, width + panel1.Padding.Horizontal + this.Padding.Horizontal + SystemInformation.VerticalScrollBarWidth);
        }

        private async Task LoadTileGroups(List<TileGroupModel> groupList)
        {
            await Task.Run(() =>
            {
                UIControl.Clear(flowLayoutPanel1);

                // Load groups
                foreach (var item in groupList ?? new List<TileGroupModel>())
                {
                    var panel = new RyzStudio.Windows.TileForms.TileContainer();
                    panel.Title = item.Title;
                    panel.IsOpen = item.IsExpanded;
                    panel.TitleContextMenuStrip = tileContainerMenu1;
                    panel.AutoSizeHeight = true;
                    panel.Tag = item;

                    panel.AutoSize(item.GridSize.Width, item.GridSize.Height);

                    UIControl.Add(flowLayoutPanel1, panel);

                    // Load tiles
                    foreach (var item2 in item.Items ?? new List<TileModel>())
                    {
                        var tile = new FizzyLauncher.Windows.Forms.TilePanel();
                        tile.LoadInfo(item2);

                        panel.Add(tile, item2.Position.X, item2.Position.Y);
                    }

                }
            });
        }

    }
}