using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Resources;

namespace TweetDuck.Core{
    sealed class TweetDeckBrowser : IDisposable{
        private static UserConfig Config => Program.Config.User;

        public bool Ready { get; private set; }

        public bool Enabled{
            get => browser.Enabled;
            set => browser.Enabled = value;
        }

        public bool IsTweetDeckWebsite{
            get{
                if (!Ready){
                    return false;
                }

                using(IFrame frame = browser.GetBrowser().MainFrame){
                    return TwitterUtils.IsTweetDeckWebsite(frame);
                }
            }
        }
        
        private readonly ChromiumWebBrowser browser;

        private string prevSoundNotificationPath = null;

        public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
            RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
            
            this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
                DialogHandler = new FileDialogHandler(),
                DragHandler = new DragHandlerBrowser(requestHandler),
                MenuHandler = new ContextMenuBrowser(owner),
                JsDialogHandler = new JavaScriptDialogHandler(),
                KeyboardHandler = new KeyboardHandlerBrowser(owner),
                LifeSpanHandler = new LifeSpanHandler(),
                RequestHandler = requestHandler
            };

            this.browser.LoadingStateChanged += browser_LoadingStateChanged;
            this.browser.FrameLoadStart += browser_FrameLoadStart;
            this.browser.FrameLoadEnd += browser_FrameLoadEnd;
            this.browser.LoadError += browser_LoadError;

            this.browser.RegisterAsyncJsObject("$TD", tdBridge);
            this.browser.RegisterAsyncJsObject("$TDU", updateBridge);
            
            this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
            this.browser.Dock = DockStyle.None;
            this.browser.Location = ControlExtensions.InvisibleLocation;

            this.browser.SetupResourceHandler(TweetNotification.AppLogo);
            this.browser.SetupResourceHandler(TwitterUtils.LoadingSpinner);
            this.browser.SetupZoomEvents();

            owner.Controls.Add(browser);
            plugins.Register(browser, PluginEnvironment.Browser, owner, true);
            
            Config.MuteToggled += Config_MuteToggled;
            Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged;
        }

        // setup and management

        private void OnBrowserReady(){
            if (!Ready){
                browser.Location = Point.Empty;
                browser.Dock = DockStyle.Fill;
                Ready = true;
            }
        }

        public void Focus(){
            browser.Focus();
        }

        public void Dispose(){
            Config.MuteToggled -= Config_MuteToggled;
            Config.SoundNotificationChanged -= Config_SoundNotificationInfoChanged;
            
            browser.Dispose();
        }

        // event handlers

        private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
            if (!e.IsLoading){
                foreach(string word in TwitterUtils.DictionaryWords){
                    browser.AddWordToDictionary(word);
                }

                browser.BeginInvoke(new Action(OnBrowserReady));
                browser.LoadingStateChanged -= browser_LoadingStateChanged;
            }
        }

        private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
            IFrame frame = e.Frame;

            if (frame.IsMain){
                if (TwitterUtils.IsTwitterWebsite(frame)){
                    ScriptLoader.ExecuteFile(frame, "twitter.js", browser);
                }
                
                frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
            }
        }

        private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
            IFrame frame = e.Frame;

            if (frame.IsMain){
                if (TwitterUtils.IsTweetDeckWebsite(frame)){
                    UpdateProperties();
                    ScriptLoader.ExecuteFile(frame, "code.js", browser);

                    InjectBrowserCSS();
                    ReinjectCustomCSS(Config.CustomBrowserCSS);
                    Config_SoundNotificationInfoChanged(null, EventArgs.Empty);

                    TweetDeckBridge.ResetStaticProperties();

                    if (Arguments.HasFlag(Arguments.ArgIgnoreGDPR)){
                        ScriptLoader.ExecuteScript(frame, "TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
                    }

                    if (Config.FirstRun){
                        ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
                    }
                }

                ScriptLoader.ExecuteFile(frame, "update.js", browser);
            }
        }

        private void browser_LoadError(object sender, LoadErrorEventArgs e){
            if (e.ErrorCode == CefErrorCode.Aborted){
                return;
            }

            if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
                string errorPage = ScriptLoader.LoadResourceSilent("pages/error.html");

                if (errorPage != null){
                    browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
                }
            }
        }

        private void Config_MuteToggled(object sender, EventArgs e){
            UpdateProperties();
        }

        private void Config_SoundNotificationInfoChanged(object sender, EventArgs e){
            const string soundUrl = "https://ton.twimg.com/tduck/updatesnd";

            bool hasCustomSound = Config.IsCustomSoundNotificationSet;
            string newNotificationPath = Config.NotificationSoundPath;

            if (prevSoundNotificationPath != newNotificationPath){
                browser.SetupResourceHandler(soundUrl, hasCustomSound ? SoundNotification.CreateFileHandler(newNotificationPath) : null);
                prevSoundNotificationPath = newNotificationPath;
            }

            browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Config.NotificationSoundVolume);
        }

        // external handling

        public void HideVideoOverlay(bool focus){
            if (focus){
                browser.GetBrowser().GetHost().SendFocusEvent(true);
            }

            browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
        }

        // javascript calls

        public void ReloadToTweetDeck(){
            browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
        }

        public void UpdateProperties(){
            browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
        }

        public void InjectBrowserCSS(){
            browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css", browser)?.TrimEnd() ?? string.Empty);
        }

        public void ReinjectCustomCSS(string css){
            browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
        }

        public void OnMouseClickExtra(IntPtr param){
            browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
        }

        public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
            browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
        }

        public void AddSearchColumn(string query){
            browser.ExecuteScriptAsync("TDGF_performSearch", query);
        }

        public void TriggerTweetScreenshot(){
            browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
        }

        public void ReloadColumns(){
            browser.ExecuteScriptAsync("TDGF_reloadColumns()");
        }

        public void PlaySoundNotification(){
            browser.ExecuteScriptAsync("TDGF_playSoundNotification()");
        }

        public void ApplyROT13(){
            browser.ExecuteScriptAsync("TDGF_applyROT13()");
        }

        public void ShowUpdateNotification(string versionTag, string releaseNotes){
            browser.ExecuteScriptAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes)));
        }

        public void OpenDevTools(){
            browser.ShowDevTools();
        }
    }
}