using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using TweetDuck.Browser;
using TweetDuck.Browser.Handling.General;
using TweetDuck.Browser.Notification.Example;
using TweetDuck.Configuration;
using TweetDuck.Controls;
using TweetDuck.Dialogs.Settings;
using TweetDuck.Management;
using TweetDuck.Management.Analytics;
using TweetDuck.Utils;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Updates;

namespace TweetDuck.Dialogs{
    sealed partial class FormSettings : Form, FormManager.IAppDialog{
        public bool ShouldReloadBrowser { get; private set; }

        private readonly FormBrowser browser;
        private readonly PluginManager plugins;

        private readonly int buttonHeight;

        private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(8);
        private SettingsTab currentTab;

        public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){
            InitializeComponent();

            Text = Program.BrandName + " Options";

            this.browser = browser;
            this.browser.PauseNotification();

            this.plugins = plugins;
            
            this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;

            PrepareLoad();

            AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadColumns, updates));
            AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
            AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification));
            AddButton("Tray", () => new TabSettingsTray());
            AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
            AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS, this.browser.OpenDevTools));

            SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
        }

        private void PrepareLoad(){
            Program.Config.ProgramRestartRequested += Config_ProgramRestartRequested;
        }

        private void PrepareUnload(){ // TODO refactor this further later
            currentTab.Control.OnClosing();
            
            Program.Config.ProgramRestartRequested -= Config_ProgramRestartRequested;
            Program.Config.SaveAll();
        }

        private void Config_ProgramRestartRequested(object sender, EventArgs e){
            if (FormMessage.Information("TweetDuck Options", "The application must restart for the option to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)){
                Program.Restart();
            }
        }

        private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
            PrepareUnload();

            foreach(SettingsTab tab in tabs.Values){
                if (tab.IsInitialized){
                    tab.Control.Dispose();
                }
            }
            
            browser.ResumeNotification();
        }

        private void btnManageOptions_Click(object sender, EventArgs e){
            PrepareUnload();

            using DialogSettingsManage dialog = new DialogSettingsManage(plugins);
            FormClosing -= FormSettings_FormClosing;
            
            if (dialog.ShowDialog() == DialogResult.OK){
                if (!dialog.IsRestarting){
                    browser.ResumeNotification();

                    if (dialog.ShouldReloadBrowser){
                        BrowserProcessHandler.UpdatePrefs();
                        ShouldReloadBrowser = true;
                    }
                }

                Close();
            }
            else{
                FormClosing += FormSettings_FormClosing;
                PrepareLoad();
            }
        }

        private void btnClose_Click(object sender, EventArgs e){
            Close();
        }

        private void AddButton<T>(string title, Func<T> constructor) where T : BaseTab{
            FlatButton btn = new FlatButton{
                BackColor = SystemColors.Control,
                FlatStyle = FlatStyle.Flat,
                Font = SystemFonts.MessageBoxFont,
                Location = new Point(0, (buttonHeight + 1) * (panelButtons.Controls.Count / 2)),
                Margin = new Padding(0),
                Size = new Size(panelButtons.Width, buttonHeight),
                Text = title,
                UseVisualStyleBackColor = true
            };

            btn.FlatAppearance.BorderSize = 0;
            btn.FlatAppearance.MouseDownBackColor = Color.FromArgb(179, 213, 232);
            btn.FlatAppearance.MouseOverBackColor = Color.FromArgb(216, 230, 237);
            
            panelButtons.Controls.Add(btn);

            panelButtons.Controls.Add(new Panel{
                BackColor = Color.DimGray,
                Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count - 1].Location.Y + buttonHeight),
                Margin = new Padding(0),
                Size = new Size(panelButtons.Width, 1)
            });

            tabs.Add(typeof(T), new SettingsTab(btn, constructor));

            btn.Click += (sender, args) => SelectTab<T>();
        }

        private void SelectTab<T>() where T : BaseTab{
            SelectTab(tabs[typeof(T)]);
        }

        private void SelectTab(SettingsTab tab){
            if (currentTab != null){
                currentTab.Button.BackColor = SystemColors.Control;
                currentTab.Control.OnClosing();
            }
            
            tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;

            if (!tab.IsInitialized){
                foreach(Control control in tab.Control.InteractiveControls){
                    if (control is ComboBox){
                        control.MouseLeave += control_MouseLeave;
                    }
                    else if (control is TrackBar){
                        control.MouseWheel += control_MouseWheel;
                    }
                }

                if (tab.Control.Height < panelContents.Height - 2){
                    tab.Control.Height = panelContents.Height - 2; // fixes off-by-pixel error on high DPI
                }

                tab.Control.OnReady();
            }
            
            panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything
            panelContents.PerformLayout();

            panelContents.SuspendLayout();
            panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte
            panelContents.Controls.Clear();
            panelContents.Controls.Add(tab.Control);
            panelContents.ResumeLayout(true);

            panelContents.VerticalScroll.Enabled = true;
            panelContents.Focus();

            currentTab = tab;
        }

        private void control_MouseLeave(object sender, EventArgs e){
            if (sender is ComboBox cb && cb.DroppedDown){
                return; // prevents comboboxes from closing when MouseLeave event triggers during opening animation
            }

            panelContents.Focus();
        }

        private void control_MouseWheel(object sender, MouseEventArgs e){
            ((HandledMouseEventArgs)e).Handled = true;
            panelContents.Focus();
        }

        private sealed class SettingsTab{
            public Button Button { get; }

            public BaseTab Control => control ??= constructor();
            public bool IsInitialized => control != null;

            private readonly Func<BaseTab> constructor;
            private BaseTab control;

            public SettingsTab(Button button, Func<BaseTab> constructor){
                this.Button = button;
                this.constructor = constructor;
            }
        }

        internal abstract class BaseTab : UserControl{
            protected static UserConfig Config => Program.Config.User;
            protected static SystemConfig SysConfig => Program.Config.System;

            public IEnumerable<Control> InteractiveControls{
                get{
                    static IEnumerable<Control> FindInteractiveControls(Control parent){
                        foreach(Control control in parent.Controls){
                            if (control is Panel subPanel){
                                foreach(Control subControl in FindInteractiveControls(subPanel)){
                                    yield return subControl;
                                }
                            }
                            else{
                                yield return control;
                            }
                        }
                    }

                    return FindInteractiveControls(this);
                }
            }

            protected BaseTab(){
                Padding = new Padding(6);
            }

            public virtual void OnReady(){}
            public virtual void OnClosing(){}
        }
    }
}