using System.Numerics;
using Voile.Rendering;
using Voile.Resources;

namespace Voile.UI.Widgets;

/// <summary>
/// A clickable button with a label.
/// </summary>
public class Button : Widget
{
    public enum ButtonState
    {
        Disabled,
        Hovered,
        Pressed,
        Focused,
        Normal
    }

    public string Text
    {
        get => _text; set
        {
            _text = value;
            MarkDirty();
        }
    }

    public ButtonState CurrentState { get; private set; } = ButtonState.Normal;

    public override Rect MinimumSize => Padding + _textSize;

    /// <summary>
    /// <see cref="FontSet"/> to use with this button.
    /// </summary>
    public FontSet FontSet { get; set; } = new();
    public Size Padding
    {
        get => _padding; set
        {
            _padding = value;
        }
    }

    public override string? StyleElementName => nameof(Button);
    public override string[]? StyleModifiers =>
    [
        CurrentState.ToString()
    ];

    public Button(string text, ResourceRef<Font> fontOverride, Action pressedAction)
    {
        _text = text;
        _pressedAction = pressedAction;

        FontSet.AddFont(fontOverride);

        MarkDirty();
        Update();
    }

    public Button(string text, FontSet fontSet)
    {
        _text = text;

        FontSet = fontSet;

        MarkDirty();
        Update();
    }

    public Button(string text, FontSet fontSet, Action pressedAction)
    {
        _text = text;
        _pressedAction = pressedAction;

        FontSet = fontSet;

        MarkDirty();
        Update();
    }

    protected override void OnRender(RenderSystem renderer, Style style)
    {
        if (_padding != style.Padding)
        {
            MarkDirty();
        }

        _padding = style.Padding ?? Voile.Size.Zero;
        var textColor = style.TextColor ?? Color.Black;

        var textPosition = new Vector2(GlobalPosition.X + Padding.Left, GlobalPosition.Y + Padding.Top);
        renderer.SetTransform(textPosition, Vector2.Zero);

        if (_suitableFont.HasValue)
        {
            renderer.DrawText(_suitableFont, _text, textColor);
        }
    }

    protected virtual void Pressed() { }

    protected override void OnInput(UIInputContext action)
    {
        bool isHovering = ContainsPoint(action.MousePosition);

        if (action.MousePressed && isHovering)
        {
            _isHeldDown = true;
            CurrentState = ButtonState.Pressed;
        }
        else if (action.MouseReleased)
        {
            if (_isHeldDown && isHovering)
            {
                _pressedAction?.Invoke();
                Pressed();
            }

            _isHeldDown = false;
            CurrentState = isHovering ? ButtonState.Hovered : ButtonState.Normal;
        }
        else
        {
            if (_isHeldDown)
            {
                CurrentState = ButtonState.Pressed; // keep showing as pressed
            }
            else if (isHovering)
            {
                CurrentState = ButtonState.Hovered;
            }
            else
            {
                CurrentState = ButtonState.Normal;
            }
        }
    }

    protected override void OnUpdate()
    {
        ResourceRef<Font> fontRef = ResourceRef<Font>.Empty();

        foreach (var c in _text)
        {
            if (FontSet.TryGetFontFor(c, out var fallbackFont))
            {
                if (fallbackFont != fontRef)
                {
                    fontRef = fallbackFont;
                }
            }
        }

        if (fontRef.HasValue)
        {
            _suitableFont = fontRef;

            var font = _suitableFont.Value;
            _textSize = font.Measure(_text);
        }

        Size = _padding + _textSize;
    }

    private Action? _pressedAction;

    private ResourceRef<Font> _suitableFont = ResourceRef<Font>.Empty();

    private string _text = "Hello, World!";
    private Rect _textSize = Rect.Zero;

    private Size _padding;

    private bool _isHeldDown;
}