using OpenTK.Mathematics;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace Engine.Input;

public class WindowInputHandler(Window parWindow) : IInputHandler
{
  public Vector2 MousePosition => parWindow.NativeWindow.MouseState.Position;

  private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
  private KeyboardState _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();

  private MouseState _previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
  private MouseState _mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();

  public void Update(double parDeltaTime)
  {
    _previousKeyboardState = _keyboardState;
    _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();

    _previousMouseState = _mouseState;
    _mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
  }

  public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
  {
    return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
  }

  public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
  {
    return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode)) &&
           !_previousKeyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
  }

  public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
  {
    return _mouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
  }

  public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
  {
    return _mouseState.IsButtonDown(MapMouseButtonCode(parButtonCode)) &&
           !_previousMouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
  }

  private static MouseButton MapMouseButtonCode(MouseButtonCode parButton)
  {
    return parButton switch
    {
      MouseButtonCode.Left => MouseButton.Left,
      MouseButtonCode.Right => MouseButton.Right,
      MouseButtonCode.Middle => MouseButton.Middle,
      _ => throw new ArgumentOutOfRangeException(nameof(parButton), parButton, null)
    };
  }

  private static Keys MapKeyCode(KeyboardButtonCode parKeyboardButtonCode)
  {
    return parKeyboardButtonCode switch
    {
      KeyboardButtonCode.A => Keys.A,
      KeyboardButtonCode.B => Keys.B,
      KeyboardButtonCode.C => Keys.C,
      KeyboardButtonCode.D => Keys.D,
      KeyboardButtonCode.E => Keys.E,
      KeyboardButtonCode.F => Keys.F,
      KeyboardButtonCode.G => Keys.G,
      KeyboardButtonCode.H => Keys.H,
      KeyboardButtonCode.I => Keys.I,
      KeyboardButtonCode.J => Keys.J,
      KeyboardButtonCode.K => Keys.K,
      KeyboardButtonCode.L => Keys.L,
      KeyboardButtonCode.M => Keys.M,
      KeyboardButtonCode.N => Keys.N,
      KeyboardButtonCode.O => Keys.O,
      KeyboardButtonCode.P => Keys.P,
      KeyboardButtonCode.Q => Keys.Q,
      KeyboardButtonCode.R => Keys.R,
      KeyboardButtonCode.S => Keys.S,
      KeyboardButtonCode.T => Keys.T,
      KeyboardButtonCode.U => Keys.U,
      KeyboardButtonCode.V => Keys.V,
      KeyboardButtonCode.W => Keys.W,
      KeyboardButtonCode.X => Keys.X,
      KeyboardButtonCode.Y => Keys.Y,
      KeyboardButtonCode.Z => Keys.Z,
      KeyboardButtonCode.Ctrl => Keys.LeftControl,
      KeyboardButtonCode.Alt => Keys.LeftAlt,
      KeyboardButtonCode.Shift => Keys.LeftShift,
      KeyboardButtonCode.Up => Keys.Up,
      KeyboardButtonCode.Down => Keys.Down,
      KeyboardButtonCode.Left => Keys.Left,
      KeyboardButtonCode.Right => Keys.Right,
      KeyboardButtonCode.Escape => Keys.Escape,
      KeyboardButtonCode.Enter => Keys.Enter,
      KeyboardButtonCode.Space => Keys.Space,
      KeyboardButtonCode.Tab => Keys.Tab,
      KeyboardButtonCode.Backspace => Keys.Backspace,
      KeyboardButtonCode.Delete => Keys.Delete,
      KeyboardButtonCode.Insert => Keys.Insert,
      KeyboardButtonCode.Home => Keys.Home,
      KeyboardButtonCode.End => Keys.End,
      KeyboardButtonCode.PageUp => Keys.PageUp,
      KeyboardButtonCode.PageDown => Keys.PageDown,
      KeyboardButtonCode.D0 => Keys.D0,
      KeyboardButtonCode.D1 => Keys.D1,
      KeyboardButtonCode.D2 => Keys.D2,
      KeyboardButtonCode.D3 => Keys.D3,
      KeyboardButtonCode.D4 => Keys.D4,
      KeyboardButtonCode.D5 => Keys.D5,
      KeyboardButtonCode.D6 => Keys.D6,
      KeyboardButtonCode.D7 => Keys.D7,
      KeyboardButtonCode.D8 => Keys.D8,
      KeyboardButtonCode.D9 => Keys.D9,
      _ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
    };
  }
}