using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Game.CodexUI.Drag;
using Theriapolis.Game.Screens;

namespace Theriapolis.Game.CodexUI.Core;

/// <summary>
/// Base class for screens implemented in CodexUI. Owns the input snapshot,
/// the root widget, the parchment background tiling, the drag-drop
/// controller, and the popover layer. Concrete screens override
/// <see cref="BuildRoot"/> to populate the widget tree.
///
/// Implements <see cref="IScreen"/> so the existing <c>ScreenManager</c>
/// can treat CodexUI screens identically to Myra-based ones.
/// </summary>
public abstract class CodexScreen : IScreen
{
    protected Game1 Game = null!;
    protected CodexInput Input { get; } = new();
    protected CodexWidget? Root;
    protected CodexAtlas Atlas { get; private set; } = null!;
    protected DragDropController DragDrop { get; } = new();

    /// <summary>The popover layer is painted last so floating panels stay above the page.</summary>
    protected Widgets.CodexHoverPopover? Popover { get; set; }

    private System.EventHandler<TextInputEventArgs>? _textInputHandler;
    private bool _layoutDirty = true;

    public virtual void Initialize(Game1 game)
    {
        Game = game;
        Atlas = game.CodexAtlas;
        if (CodexFonts.DisplayLarge is null)
            throw new System.InvalidOperationException("CodexFonts.LoadAll must be called before any CodexScreen is initialized.");

        _textInputHandler = (_, e) =>
        {
            // Filter out non-printable controls so backspace etc. routes via
            // KeyJustPressed instead of pushing into the text buffer.
            if (e.Character >= 32 && e.Character != 127) Input.OnTextInput(e.Character);
        };
        game.Window.TextInput += _textInputHandler;

        Root = BuildRoot();
        _layoutDirty = true;
    }

    public virtual void Deactivate()
    {
        if (_textInputHandler is not null) Game.Window.TextInput -= _textInputHandler;
    }

    public virtual void Reactivate() { _layoutDirty = true; }

    /// <summary>Concrete screens build the entire widget tree here. Called once after Initialize.</summary>
    protected abstract CodexWidget BuildRoot();

    /// <summary>Force a re-measure on next frame (e.g. step changed).</summary>
    public void InvalidateLayout()
    {
        Root = BuildRoot();
        _layoutDirty = true;
    }

    public virtual void Update(GameTime gameTime)
    {
        Input.Tick();
        if (Root is null) return;

        if (_layoutDirty)
        {
            var vp = Game.GraphicsDevice.Viewport;
            Root.Measure(new Point(vp.Width, vp.Height));
            Root.Arrange(new Rectangle(0, 0, vp.Width, vp.Height));
            _layoutDirty = false;
        }
        Root.Update(gameTime, Input);
        Popover?.Update(gameTime, Input);
        DragDrop.Update(gameTime, Input);
    }

    public virtual void Draw(GameTime gameTime, SpriteBatch sb)
    {
        Game.GraphicsDevice.Clear(CodexColors.BgDeep);
        sb.Begin();
        DrawBackground(sb);
        Root?.Draw(sb, gameTime);
        Popover?.Draw(sb, gameTime);
        DragDrop.Draw(sb);
        sb.End();
    }

    /// <summary>
    /// Paint a flat <see cref="CodexColors.BgDeep"/> across the full viewport.
    /// The body widget paints its own lighter <see cref="CodexColors.Bg"/>
    /// fill on top so cards (which use the slightly-darker
    /// <see cref="CodexColors.Bg2"/>) read clearly against their immediate
    /// background. We don't tile a parchment-grain texture: a 256-px tile
    /// produces visible seams, and the procedural radial gradients made
    /// some areas brighter than the cards on top of them.
    /// </summary>
    private void DrawBackground(SpriteBatch sb)
    {
        var vp = Game.GraphicsDevice.Viewport;
        sb.Draw(Atlas.Pixel, vp.Bounds, CodexColors.BgDeep);
    }
}
