using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Theriapolis.Game.Input;

/// <summary>
/// Single-frame input snapshot with helper methods.
/// Call Update() once per frame before any input queries.
/// </summary>
public sealed class InputManager
{
    private KeyboardState _prevKeys;
    private KeyboardState _currKeys;
    private MouseState    _prevMouse;
    private MouseState    _currMouse;

    public void Update()
    {
        _prevKeys  = _currKeys;
        _currKeys  = Keyboard.GetState();
        _prevMouse = _currMouse;
        _currMouse = Mouse.GetState();
    }

    // ── Keyboard ──────────────────────────────────────────────────────────────
    public bool IsDown(Keys key)         => _currKeys.IsKeyDown(key);
    public bool JustPressed(Keys key)    => _currKeys.IsKeyDown(key)  && _prevKeys.IsKeyUp(key);
    public bool JustReleased(Keys key)   => _currKeys.IsKeyUp(key)    && _prevKeys.IsKeyDown(key);

    // ── Mouse ─────────────────────────────────────────────────────────────────
    public Vector2 MousePosition  => new(_currMouse.X, _currMouse.Y);
    public bool    LeftDown       => _currMouse.LeftButton  == ButtonState.Pressed;
    public bool    LeftJustDown   => _currMouse.LeftButton  == ButtonState.Pressed   && _prevMouse.LeftButton  == ButtonState.Released;
    public bool    LeftJustUp     => _currMouse.LeftButton  == ButtonState.Released  && _prevMouse.LeftButton  == ButtonState.Pressed;
    public bool    RightDown      => _currMouse.RightButton == ButtonState.Pressed;
    public bool    RightJustDown  => _currMouse.RightButton == ButtonState.Pressed   && _prevMouse.RightButton == ButtonState.Released;

    /// <summary>Mouse wheel delta in scroll "ticks" (positive = forward/up).</summary>
    public int ScrollDelta => _currMouse.ScrollWheelValue - _prevMouse.ScrollWheelValue;

    private Vector2 _dragStart;
    private bool    _dragging;
    private bool    _dragActivated;
    private const float DragActivationPixels = 4f;
    public bool     IsDragging => _dragging;

    /// <summary>
    /// Returns the world-space pan delta from mouse dragging. Panning is
    /// suppressed until the mouse moves more than <see cref="DragActivationPixels"/>
    /// from the press position, so hand-jitter during a click doesn't pan the
    /// camera (at low zoom, one screen pixel can be many world pixels).
    /// </summary>
    public Vector2 ConsumeDragDelta(Rendering.Camera2D camera)
    {
        if (LeftJustDown)
        {
            _dragStart     = MousePosition;
            _dragging      = true;
            _dragActivated = false;
        }
        if (LeftJustUp)
        {
            _dragging      = false;
            _dragActivated = false;
        }

        if (!_dragging || !LeftDown) return Vector2.Zero;

        if (!_dragActivated)
        {
            if (Vector2.Distance(MousePosition, _dragStart) < DragActivationPixels)
                return Vector2.Zero;
            _dragActivated = true;
            _dragStart     = MousePosition; // start panning from here, not from press
        }

        Vector2 delta = MousePosition - _dragStart;
        _dragStart    = MousePosition;
        return -delta / camera.Zoom;
    }
}
