#pragma once

#include "utils/Coords.h"

#include <SDL3/SDL.h>
#include <unordered_map>
#include <vector>

namespace quokka_gfx
{
//--------------------------------------------------------------
class InputManager
{
  friend class WindowManager;
  friend class Window;
  friend class EventManager;

  public:
  enum class MouseButton : uint8_t
  {
    Left   = SDL_BUTTON_LEFT,       // Left mouse button
    Middle = SDL_BUTTON_MIDDLE,     // Middle mouse button
    Right  = SDL_BUTTON_RIGHT,      // Right mouse button
    X1     = SDL_BUTTON_X1,         // Extra mouse button 1
    X2     = SDL_BUTTON_X2          // Extra mouse button 2
  };

  using KeyList = std::vector<SDL_Scancode>;     // List of SDL scancodes representing keys

  public:
  InputManager()                                       = default;     // Default constructor
  virtual ~InputManager()                              = default;     // Default destructor
  InputManager(const InputManager &obj)                = delete;      // Copy constructor
  InputManager(InputManager &&obj) noexcept            = delete;      // Move constructor
  InputManager &operator=(const InputManager &obj)     = delete;      // Copy assignment operator
  InputManager &operator=(InputManager &&obj) noexcept = delete;      // Move assignment operator

  // Key state query functions
  [[nodiscard]] bool isKeyDown(SDL_Scancode code) const;          // Check if a key is being held down
  [[nodiscard]] bool isKeyPressed(SDL_Scancode code) const;       // Check if a key is pressed (transition from up to down on this frame)
  [[nodiscard]] bool isKeyReleased(SDL_Scancode code) const;      // Check if a key is released (transition from down to up on this frame)
  void               getHoldingKeys(KeyList &keyList) const;      // Get a list of keys that are currently being held down
  void               getPressedKeys(KeyList &keyList) const;      // Get a list of keys that were pressed in the current frame
  void               getReleasedKeys(KeyList &keyList) const;     // Get a list of keys that were released in the current frame

  // Mouse state query functions
  [[nodiscard]] FPos getMousePosition() const;                            // Get the current mouse position
  [[nodiscard]] bool isMouseButtonDown(MouseButton button) const;         // Check if a mouse button is being held down
  [[nodiscard]] bool isMouseButtonPressed(MouseButton button) const;      // Check if a mouse button is pressed (transition from up to down on this frame)
  [[nodiscard]] bool isMouseButtonReleased(MouseButton button) const;     // Check if a mouse button is released (transition from down to up on this frame)

  [[nodiscard]] bool  isMouseWheeling() const;     // Check if the mouse wheel has been scrolled in the current frame
  [[nodiscard]] float getMouseWheelX() const;      // Get the mouse wheel delta for horizontal scrolling
  [[nodiscard]] float getMouseWheelY() const;      // Get the mouse wheel delta for vertical scrolling

  // Window state query functions
  [[nodiscard]] uint32_t getFocusedWindowID() const;     // Get the ID of the window currently focused for keyboard input
  [[nodiscard]] uint32_t getHoveredWindowID() const;     // Get the ID of the window currently hovered by the mouse

  protected:
  void updateStates();                           // Update the input states (to be called each frame)
  void processEvent(const SDL_Event &event);     // Process an SDL event to update input states

  // States
  std::unordered_map<SDL_Scancode, bool> m_currentKeys;
  std::unordered_map<SDL_Scancode, bool> m_previousKeys;
  uint32_t                               m_focusedWindowID = 0;     // Keyboard focus window ID

  std::unordered_map<MouseButton, bool> m_currentMouseButtons;
  std::unordered_map<MouseButton, bool> m_previousMouseButtons;
  FPos                                  m_mousePosition;
  uint32_t                              m_hoveredWindowID = 0;     // Mouse hover window ID

  float m_wheelX = 0.0f;     // Mouse wheel delta for horizontal scrolling
  float m_wheelY = 0.0f;     // Mouse wheel delta for vertical scrolling

  private:
  void clearWheelDelta();                                       // Clear the mouse wheel delta
  void processWheelEvent(const SDL_MouseWheelEvent &event);     // Process a mouse wheel event to update the wheel delta
};
//--------------------------------------------------------------
}     // namespace quokka_gfx
