using CefSharpWrap.AceEditorWrap.Resources;
using System;
using System.Windows.Forms;

namespace CefSharpWrap.AceEditorWrap
{
    public sealed class CefSharpAceEditorWrap
    {
        private static readonly string AceUrlPath = $"{nameof(CefSharpAceEditorWrap)}-{Guid.NewGuid()}";
        private static readonly string AceUrlBase = $"{CefSharpWrapFactory.SchemeName}://{CefSharpWrapFactory.CustomResourceDomainName}/{AceUrlPath}";
        private static readonly string AceUrlHome = $"{AceUrlBase}/AceEditorPage.html";

        private readonly AceEditorJsObjectBridge _bridge;
        private readonly CefSharpWrapBrowser _cefSharpWrapBrowser;

        /*
         *  - [X] Text change event:   OK, should double check to both direction
         *  - [X] Mode Property:       OK, readonly to cef
         *  - [X] Theme Property:      OK, readonly to cef
         *  - [X] Scroll change event: OK, dom,window care
         *  - [ ] Readonly:            OK, readonly to cef
         */

        private string _text = "";
        private AceEditorMode _mode = AceEditorMode.Markdown;
        private AceEditorTheme _theme = AceEditorTheme.Xcode;
        private int _fontSize = 20;
        private double _scrollPosition;
        private bool _readonly;

        public bool Readonly
        {
            get => _readonly;
            set
            {
                if (_readonly == value) return;
                _readonly = value;
                _cefSharpWrapBrowser.ExecuteJsAsync("syncReadonly()");
            }
        }

        public string Text
        {
            get => _text;
            set
            {
                if (_text == value) return;
                _text = value;
                _cefSharpWrapBrowser.ExecuteJsAsync("syncText();");
                OnTextChanged();
            }
        }

        public AceEditorMode Mode
        {
            get => _mode;
            set
            {
                if (_mode == value) return;
                _mode = value;
                _cefSharpWrapBrowser.ExecuteJsAsync("syncMode();");
            }
        }

        public AceEditorTheme Theme
        {
            get => _theme;
            set
            {
                if (_theme == value) return;
                _theme = value;
                _cefSharpWrapBrowser.ExecuteJsAsync("syncTheme();");
            }
        }

        public int FontSize
        {
            get => _fontSize;
            set
            {
                if (_fontSize == value) return;
                _fontSize = value;
                _cefSharpWrapBrowser.ExecuteJsAsync("syncFontSize();");
            }
        }

        public double ScrollPosition
        {
            get => _scrollPosition;
            internal set
            {
                if (Math.Abs(_scrollPosition - value) < 0.00000000000001) return;
                _scrollPosition = value;
                OnScrollPositionChanged();
            }
        }

        public event EventHandler TextChanged;

        public event EventHandler ScrollPositionChanged;

        static CefSharpAceEditorWrap()
        {
            CefSharpWrapFactory.AddCustomResourceResolver($"/{AceUrlPath}", EmbeddedResourceManager.ResourceResolver);
        }

        public CefSharpAceEditorWrap()
        {
            _cefSharpWrapBrowser = CefSharpWrapFactory.CreateSealedInstance(AceUrlHome);

            _bridge = new AceEditorJsObjectBridge(this);

            _cefSharpWrapBrowser.Resize += Underlying_Resize;
            _cefSharpWrapBrowser.RegisterJsObject("CefSharpAceWrapBridge", _bridge);
        }

        private void Underlying_Resize(object sender, EventArgs e)
        {
            _cefSharpWrapBrowser.ExecuteJsAsync("syncSize();");
        }

        private void OnTextChanged()
        {
            TextChanged?.Invoke(this, EventArgs.Empty);
        }

        private void OnScrollPositionChanged()
        {
            ScrollPositionChanged?.Invoke(this, EventArgs.Empty);
        }

        private class AceEditorJsObjectBridge
        {
            private readonly CefSharpAceEditorWrap _wrapInstance;

            public AceEditorJsObjectBridge(CefSharpAceEditorWrap wrapInstance)
            {
                _wrapInstance = wrapInstance;
            }

            // ReSharper disable UnusedMember.Local

            public string Text
            {
                get => _wrapInstance.Text;
                set => _wrapInstance.Text = value;
            }

            public bool Readonly => _wrapInstance.Readonly;

            public int FontSize => _wrapInstance.FontSize;
            public string Mode => _wrapInstance.Mode.GetEnumValue();
            public string Theme => _wrapInstance.Theme.GetEnumValue();

            public double ScrollPosition
            {
                set => _wrapInstance.ScrollPosition = value;
            }

            // ReSharper restore UnusedMember.Local
        }

        public void FillToControl(Control to)
        {
            _cefSharpWrapBrowser.FillToControl(to);
        }

        public void ShowF12()
        {
            _cefSharpWrapBrowser.ShowF12();
        }

        public void Refresh()
        {
            _cefSharpWrapBrowser.LoadUrl(AceUrlHome);
        }
    }
}