#nullable enable

using System;
using System.IO;
using System.Windows; // WPF UIElement
using System.Windows.Forms;
using System.Windows.Forms.Integration;

using VL.Core.Import;
using VL.HDE;
using VL.HDE.Forms;

namespace MyCodeEditor.HDEUtils
{
    /// <summary>
    /// Dockable HDE window that hosts WPF content via ElementHost.
    /// Create phase = public constructor (WindowContext comes here).
    /// Update phase = public Update overloads (WindowFactory may call every frame).
    ///
    /// IMPORTANT:
    /// Do NOT push text into the editor every frame, otherwise you overwrite user edits.
    /// Use ApplyText as an explicit gate (pulse) when you want to load/replace text.
    /// </summary>
    [ProcessNode(Name = "DockedWpfWindow", Category = "HDE")]
    public sealed class DockedWpfWindow : IDisposable
    {
        private readonly WpfDockForm _form;
        private readonly RoslynPadEditorView _editorView;

        private string _lastAppliedText = string.Empty;
        private bool _hasAppliedText;

        /// <summary>
        /// Create phase (white pins): WindowFactory passes WindowContext here.
        /// Keep Create minimal: only context.
        /// </summary>
        public DockedWpfWindow(WindowContext context)
        {
            // Ensure assembly redirect is initialized before creating RoslynPad editor
            RoslynAssemblyRedirect.EnsureInitialized();

            _form = new WpfDockForm(context);

            _editorView = new RoslynPadEditorView();
            _editorView.SetWorkingDirectory(Path.GetTempPath());
            _editorView.SetVirtualDocumentPath("inmemory://vvvv-gamma/code-editor.cs");

            // Set an initial demo text once (safe).
            const string demo =
                "using System;\n\n" +
                "// RoslynPad inside HDE\n" +
                "public static class Test\n" +
                "{\n" +
                "    public static void Main() => Console.WriteLine(\"Hello\");\n" +
                "}\n";

            _editorView.SetText(demo);
            _lastAppliedText = demo;
            _hasAppliedText = true;

            _form.SetTitleSafe("Code Editor");
            _form.SetWpfContentSafe(_editorView);

            // Basic theme bridge.
            _editorView.ApplyHostColors(_form.BackColor, _form.ForeColor);

            // Keep colors in sync if Gamma theme changes.
            _form.BackColorChanged += (_, __) => _editorView.ApplyHostColors(_form.BackColor, _form.ForeColor);
            _form.ForeColorChanged += (_, __) => _editorView.ApplyHostColors(_form.BackColor, _form.ForeColor);
        }

        /// <summary>
        /// Smallest overload: only visibility.
        /// Use this if your WindowFactory patch only wires Show.
        /// </summary>
        public IWindow Update(bool show)
        {
            _form.SetVisibilitySafe(show, bringToFront: false);
            _editorView.ApplyHostColors(_form.BackColor, _form.ForeColor);
            return _form;
        }

        /// <summary>
        /// Overload that also returns current text (read-only, no overwriting).
        /// </summary>
        public IWindow Update(bool show, out string currentText)
        {
            _form.SetVisibilitySafe(show, bringToFront: false);
            _editorView.ApplyHostColors(_form.BackColor, _form.ForeColor);
            currentText = _editorView.GetText();
            return _form;
        }

        /// <summary>
        /// Full overload:
        /// - Title is optional (null/empty -> unchanged)
        /// - ApplyText gates whether Text is written into the editor this frame
        /// - Text can be empty; ApplyText controls whether empty means "clear" or "ignore"
        /// - Returns currentText every frame for syncing back to your object model
        /// </summary>
        public IWindow Update(
            bool show,
            string? title,
            bool applyText,
            string? text,
            bool bringToFront,
            out string currentText)
        {
            _form.SetTitleSafe(title);
            _form.SetVisibilitySafe(show, bringToFront);

            // Only write into editor when explicitly requested.
            // This prevents per-frame overwriting of user edits.
            if (applyText)
            {
                var newText = text ?? string.Empty;

                // Avoid redundant assignments.
                if (!_hasAppliedText || !string.Equals(newText, _lastAppliedText, StringComparison.Ordinal))
                {
                    _editorView.SetText(newText);
                    _lastAppliedText = newText;
                    _hasAppliedText = true;
                }
            }

            _editorView.ApplyHostColors(_form.BackColor, _form.ForeColor);

            currentText = _editorView.GetText();
            return _form;
        }

        public IWindow Window => _form;

        public void Dispose()
        {
            try { _editorView.Dispose(); } catch { /* ignore */ }
            try { _form.Dispose(); } catch { /* ignore */ }
        }

        /// <summary>
        /// WinForms-docked HDE form that hosts a WPF UIElement via ElementHost.
        /// </summary>
        private sealed class WpfDockForm : HDEForm
        {
            private readonly ElementHost _elementHost;

            public WpfDockForm(WindowContext context)
                : base(context)
            {
                // Many docking systems prefer Hide-on-close behavior.
                try { HideOnClose = true; } catch { /* ignore if not available */ }

                _elementHost = new ElementHost
                {
                    Dock = DockStyle.Fill
                };

                Controls.Add(_elementHost);
            }

            public void SetTitleSafe(string? title)
            {
                void Action()
                {
                    if (!string.IsNullOrWhiteSpace(title))
                        Text = title!;
                }

                if (InvokeRequired) BeginInvoke((Action)Action);
                else Action();
            }

            public void SetWpfContentSafe(UIElement element)
            {
                void Action()
                {
                    _elementHost.Child = element;
                }

                if (InvokeRequired) BeginInvoke((Action)Action);
                else Action();
            }

            public void SetVisibilitySafe(bool show, bool bringToFront)
            {
                void Action()
                {
                    if (show)
                    {
                        if (!Visible) Show();
                        if (bringToFront) Activate();
                    }
                    else
                    {
                        if (Visible) Hide();
                    }
                }

                if (InvokeRequired) BeginInvoke((Action)Action);
                else Action();
            }

            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    try { _elementHost.Child = null; } catch { /* ignore */ }
                    try { _elementHost.Dispose(); } catch { /* ignore */ }
                }

                base.Dispose(disposing);
            }
        }
    }
}
