using System.Windows;
using System.Windows.Input;
using Engine.Input;
using OpenTK.Mathematics;

namespace PresenterWpf;

public class WpfInputHandler : IInputHandler
{
  public Vector2 MousePosition => _mousePosition;

  private readonly Window _window;

  private readonly bool[] _actualKeys = new bool[(int)KeyboardButtonCode.TotalCount];
  private readonly bool[] _currentKeys = new bool[(int)KeyboardButtonCode.TotalCount];
  private readonly bool[] _previousKeys = new bool[(int)KeyboardButtonCode.TotalCount];

  private readonly bool[] _actualMouseButtons = new bool[(int)MouseButtonCode.TotalCount];
  private readonly bool[] _currentMouseButtons = new bool[(int)MouseButtonCode.TotalCount];
  private readonly bool[] _previousMouseButtons = new bool[(int)MouseButtonCode.TotalCount];


  private Vector2 _mousePosition = Vector2.Zero;

  public WpfInputHandler(Window parWindow)
  {
    _window = parWindow;

    _window.PreviewKeyDown += Window_PreviewKeyDown;
    _window.PreviewKeyUp += Window_PreviewKeyUp;
    _window.PreviewMouseDown += Window_PreviewMouseDown;
    _window.PreviewMouseUp += Window_PreviewMouseUp;
    _window.MouseMove += Window_MouseMove;
  }

  public void Update(double parDeltaTime)
  {
    for (var i = 0; i < _currentKeys.Length; i++)
    {
      _previousKeys[i] = _currentKeys[i];
      _currentKeys[i] = _actualKeys[i];
    }

    for (var i = 0; i < _currentMouseButtons.Length; i++)
    {
      _previousMouseButtons[i] = _currentMouseButtons[i];
      _currentMouseButtons[i] = _actualMouseButtons[i];
    }
  }

  private void Window_PreviewKeyDown(object parSender, KeyEventArgs parEventArgs)
  {
    var keyCode = ConvertToKeyboardButtonCode(parEventArgs.Key);
    if (keyCode >= 0 && keyCode < _actualKeys.Length)
    {
      _actualKeys[keyCode] = true;
    }
  }

  private void Window_PreviewKeyUp(object parSender, KeyEventArgs parEventArgs)
  {
    var keyCode = ConvertToKeyboardButtonCode(parEventArgs.Key);
    if (keyCode >= 0 && keyCode < _actualKeys.Length)
    {
      _actualKeys[keyCode] = false;
    }
  }

  private void Window_PreviewMouseDown(object parSender, MouseButtonEventArgs parEventArgs)
  {
    var buttonCode = ConvertToMouseButtonCode(parEventArgs.ChangedButton);
    if (buttonCode >= 0 && buttonCode < _actualMouseButtons.Length)
    {
      _actualMouseButtons[buttonCode] = true;
    }
  }

  private void Window_PreviewMouseUp(object parSender, MouseButtonEventArgs parEventArgs)
  {
    var buttonCode = ConvertToMouseButtonCode(parEventArgs.ChangedButton);
    if (buttonCode >= 0 && buttonCode < _actualMouseButtons.Length)
    {
      _actualMouseButtons[buttonCode] = false;
    }
  }

  private void Window_MouseMove(object parSender, MouseEventArgs parEventArgs)
  {
    var position = parEventArgs.GetPosition(null);
    _mousePosition = new Vector2((float)position.X, (float)position.Y);
  }

  public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
  {
    var keyCode = (int)parKeyboardButtonCode;
    return _currentKeys[keyCode];
  }

  public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
  {
    var keyCode = (int)parKeyboardButtonCode;
    return _currentKeys[keyCode] && !_previousKeys[keyCode];
  }

  public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
  {
    var buttonCode = (int)parButtonCode;
    return _currentMouseButtons[buttonCode];
  }

  public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
  {
    var buttonCode = (int)parButtonCode;
    return _currentMouseButtons[buttonCode] && !_previousMouseButtons[buttonCode];
  }

  private static int ConvertToKeyboardButtonCode(Key parKey) => parKey switch
  {
    Key.A => (int)KeyboardButtonCode.A,
    Key.B => (int)KeyboardButtonCode.B,
    Key.C => (int)KeyboardButtonCode.C,
    Key.D => (int)KeyboardButtonCode.D,
    Key.E => (int)KeyboardButtonCode.E,
    Key.F => (int)KeyboardButtonCode.F,
    Key.G => (int)KeyboardButtonCode.G,
    Key.H => (int)KeyboardButtonCode.H,
    Key.I => (int)KeyboardButtonCode.I,
    Key.J => (int)KeyboardButtonCode.J,
    Key.K => (int)KeyboardButtonCode.K,
    Key.L => (int)KeyboardButtonCode.L,
    Key.M => (int)KeyboardButtonCode.M,
    Key.N => (int)KeyboardButtonCode.N,
    Key.O => (int)KeyboardButtonCode.O,
    Key.P => (int)KeyboardButtonCode.P,
    Key.Q => (int)KeyboardButtonCode.Q,
    Key.R => (int)KeyboardButtonCode.R,
    Key.S => (int)KeyboardButtonCode.S,
    Key.T => (int)KeyboardButtonCode.T,
    Key.U => (int)KeyboardButtonCode.U,
    Key.V => (int)KeyboardButtonCode.V,
    Key.W => (int)KeyboardButtonCode.W,
    Key.X => (int)KeyboardButtonCode.X,
    Key.Y => (int)KeyboardButtonCode.Y,
    Key.Z => (int)KeyboardButtonCode.Z,
    Key.LeftCtrl => (int)KeyboardButtonCode.Ctrl,
    Key.LeftAlt => (int)KeyboardButtonCode.Alt,
    Key.LeftShift => (int)KeyboardButtonCode.Shift,
    Key.RightCtrl => (int)KeyboardButtonCode.Ctrl,
    Key.RightAlt => (int)KeyboardButtonCode.Alt,
    Key.RightShift => (int)KeyboardButtonCode.Shift,
    Key.Space => (int)KeyboardButtonCode.Space,
    Key.Tab => (int)KeyboardButtonCode.Tab,
    Key.Back => (int)KeyboardButtonCode.Backspace,
    Key.Return => (int)KeyboardButtonCode.Enter,
    Key.Escape => (int)KeyboardButtonCode.Escape,
    Key.PageUp => (int)KeyboardButtonCode.PageUp,
    Key.PageDown => (int)KeyboardButtonCode.PageDown,
    Key.Up => (int)KeyboardButtonCode.Up,
    Key.Down => (int)KeyboardButtonCode.Down,
    Key.Left => (int)KeyboardButtonCode.Left,
    Key.Right => (int)KeyboardButtonCode.Right,
    Key.Delete => (int)KeyboardButtonCode.Delete,
    Key.Insert => (int)KeyboardButtonCode.Insert,
    Key.Home => (int)KeyboardButtonCode.Home,
    Key.End => (int)KeyboardButtonCode.End,
    Key.D0 => (int)KeyboardButtonCode.D0,
    Key.D1 => (int)KeyboardButtonCode.D1,
    Key.D2 => (int)KeyboardButtonCode.D2,
    Key.D3 => (int)KeyboardButtonCode.D3,
    Key.D4 => (int)KeyboardButtonCode.D4,
    Key.D5 => (int)KeyboardButtonCode.D5,
    Key.D6 => (int)KeyboardButtonCode.D6,
    Key.D7 => (int)KeyboardButtonCode.D7,
    Key.D8 => (int)KeyboardButtonCode.D8,
    Key.D9 => (int)KeyboardButtonCode.D9,
    _ => -1
  };

  private static int ConvertToMouseButtonCode(MouseButton parButton) =>
    parButton switch
    {
      MouseButton.Left => (int)MouseButtonCode.Left,
      MouseButton.Right => (int)MouseButtonCode.Right,
      MouseButton.Middle => (int)MouseButtonCode.Middle,
      _ => -1
    };

  ~WpfInputHandler()
  {
    _window.PreviewKeyDown -= Window_PreviewKeyDown;
    _window.PreviewKeyUp -= Window_PreviewKeyUp;
    _window.PreviewMouseDown -= Window_PreviewMouseDown;
    _window.PreviewMouseUp -= Window_PreviewMouseUp;
  }
}