#pragma once

#include <SDL.h>
#include <array>
#include <core/service.h>
#include <types/base/types.h>
#include <types/ptr/ref_counted.h>
#include <vector>

namespace extra2d {

/**
 * @brief 键键码别名
 */
using Key = SDL_Scancode;

/**
 * @brief 按键常量
 */
namespace Keys {
constexpr Key Unknown = SDL_SCANCODE_UNKNOWN;
constexpr Key A = SDL_SCANCODE_A;
constexpr Key B = SDL_SCANCODE_B;
constexpr Key C = SDL_SCANCODE_C;
constexpr Key D = SDL_SCANCODE_D;
constexpr Key E = SDL_SCANCODE_E;
constexpr Key F = SDL_SCANCODE_F;
constexpr Key G = SDL_SCANCODE_G;
constexpr Key H = SDL_SCANCODE_H;
constexpr Key I = SDL_SCANCODE_I;
constexpr Key J = SDL_SCANCODE_J;
constexpr Key K = SDL_SCANCODE_K;
constexpr Key L = SDL_SCANCODE_L;
constexpr Key M = SDL_SCANCODE_M;
constexpr Key N = SDL_SCANCODE_N;
constexpr Key O = SDL_SCANCODE_O;
constexpr Key P = SDL_SCANCODE_P;
constexpr Key Q = SDL_SCANCODE_Q;
constexpr Key R = SDL_SCANCODE_R;
constexpr Key S = SDL_SCANCODE_S;
constexpr Key T = SDL_SCANCODE_T;
constexpr Key U = SDL_SCANCODE_U;
constexpr Key V = SDL_SCANCODE_V;
constexpr Key W = SDL_SCANCODE_W;
constexpr Key X = SDL_SCANCODE_X;
constexpr Key Y = SDL_SCANCODE_Y;
constexpr Key Z = SDL_SCANCODE_Z;
constexpr Key Num0 = SDL_SCANCODE_0;
constexpr Key Num1 = SDL_SCANCODE_1;
constexpr Key Num2 = SDL_SCANCODE_2;
constexpr Key Num3 = SDL_SCANCODE_3;
constexpr Key Num4 = SDL_SCANCODE_4;
constexpr Key Num5 = SDL_SCANCODE_5;
constexpr Key Num6 = SDL_SCANCODE_6;
constexpr Key Num7 = SDL_SCANCODE_7;
constexpr Key Num8 = SDL_SCANCODE_8;
constexpr Key Num9 = SDL_SCANCODE_9;
constexpr Key F1 = SDL_SCANCODE_F1;
constexpr Key F2 = SDL_SCANCODE_F2;
constexpr Key F3 = SDL_SCANCODE_F3;
constexpr Key F4 = SDL_SCANCODE_F4;
constexpr Key F5 = SDL_SCANCODE_F5;
constexpr Key F6 = SDL_SCANCODE_F6;
constexpr Key F7 = SDL_SCANCODE_F7;
constexpr Key F8 = SDL_SCANCODE_F8;
constexpr Key F9 = SDL_SCANCODE_F9;
constexpr Key F10 = SDL_SCANCODE_F10;
constexpr Key F11 = SDL_SCANCODE_F11;
constexpr Key F12 = SDL_SCANCODE_F12;
constexpr Key Space = SDL_SCANCODE_SPACE;
constexpr Key Enter = SDL_SCANCODE_RETURN;
constexpr Key Escape = SDL_SCANCODE_ESCAPE;
constexpr Key Tab = SDL_SCANCODE_TAB;
constexpr Key Backspace = SDL_SCANCODE_BACKSPACE;
constexpr Key Insert = SDL_SCANCODE_INSERT;
constexpr Key Delete = SDL_SCANCODE_DELETE;
constexpr Key Home = SDL_SCANCODE_HOME;
constexpr Key End = SDL_SCANCODE_END;
constexpr Key PageUp = SDL_SCANCODE_PAGEUP;
constexpr Key PageDown = SDL_SCANCODE_PAGEDOWN;
constexpr Key Left = SDL_SCANCODE_LEFT;
constexpr Key Right = SDL_SCANCODE_RIGHT;
constexpr Key Up = SDL_SCANCODE_UP;
constexpr Key Down = SDL_SCANCODE_DOWN;
constexpr Key LeftShift = SDL_SCANCODE_LSHIFT;
constexpr Key RightShift = SDL_SCANCODE_RSHIFT;
constexpr Key LeftCtrl = SDL_SCANCODE_LCTRL;
constexpr Key RightCtrl = SDL_SCANCODE_RCTRL;
constexpr Key LeftAlt = SDL_SCANCODE_LALT;
constexpr Key RightAlt = SDL_SCANCODE_RALT;
} // namespace Keys

/**
 * @brief 鼠标按键
 */
enum class MouseBtn : uint8 {
  Left = 0,
  Middle = 1,
  Right = 2,
  X1 = 3,
  X2 = 4,
  Count = 5
};

/**
 * @brief 游戏手柄按键
 */
enum class GamepadBtn : uint8 {
  A = 0,
  B = 1,
  X = 2,
  Y = 3,
  Back = 4,
  Guide = 5,
  Start = 6,
  LeftStick = 7,
  RightStick = 8,
  LeftShoulder = 9,
  RightShoulder = 10,
  DPadUp = 11,
  DPadDown = 12,
  DPadLeft = 13,
  DPadRight = 14,
  Count = 15
};

/**
 * @brief 游戏手柄轴
 */
enum class GamepadAxis : uint8 {
  LeftX = 0,
  LeftY = 1,
  RightX = 2,
  RightY = 3,
  TriggerLeft = 4,
  TriggerRight = 5,
  Count = 6
};

/**
 * @brief 触摸状态
 */
enum class TouchState : uint8 { None = 0, Began, Moved, Ended, Cancelled };

/**
 * @brief 触摸点信息
 */
struct TouchPoint {
  int64 id = 0;
  float x = 0.0f;
  float y = 0.0f;
  float prevX = 0.0f;
  float prevY = 0.0f;
  float deltaX = 0.0f;
  float deltaY = 0.0f;
  TouchState state = TouchState::None;
  float pressure = 1.0f;
};

/**
 * @brief 按键回调类型
 */
using KeyCb = Fn<void(Key)>;
using MouseBtnCb = Fn<void(MouseBtn, int32 x, int32 y)>;
using TouchCb = Fn<void(const TouchPoint &)>;

/**
 * @brief 输入服务
 *
 * 管理键盘、鼠标、触摸、游戏手柄输入
 */
class InputSvc : public IService {
public:
  /**
   * @brief 获取单例实例
   */
  static InputSvc &inst();

  const char *name() const override { return "InputSvc"; }
  int pri() const override { return Pri::Input; }

  bool init() override;
  void shutdown() override;
  void update(float dt) override;

  // ========== 键盘 ==========

  /**
   * @brief 检查按键是否按下
   */
  bool isKeyDown(Key key) const;

  /**
   * @brief 检查按键是否刚按下
   */
  bool isKeyPressed(Key key) const;

  /**
   * @brief 检查按键是否刚释放
   */
  bool isKeyReleased(Key key) const;

  // ========== 鼠标 ==========

  /**
   * @brief 获取鼠标位置
   */
  void getMousePos(int32 &x, int32 &y) const;

  /**
   * @brief 获取鼠标位置（浮点）
   */
  void getMousePos(float &x, float &y) const;

  /**
   * @brief 检查鼠标按键是否按下
   */
  bool isMouseBtnDown(MouseBtn btn) const;

  /**
   * @brief 检查鼠标按键是否刚按下
   */
  bool isMouseBtnPressed(MouseBtn btn) const;

  /**
   * @brief 检查鼠标按键是否刚释放
   */
  bool isMouseBtnReleased(MouseBtn btn) const;

  /**
   * @brief 获取鼠标滚轮
   */
  int32 getMouseWheel() const;

  // ========== 触摸 ==========

  /**
   * @brief 获取触摸点数量
   */
  int32 touchCount() const;

  /**
   * @brief 获取触摸点
   * @param idx 触摸点索引
   * @return 触摸点信息，无效索引返回 nullptr
   */
  const TouchPoint *getTouch(int32 idx) const;

  /**
   * @brief 根据 ID 获取触摸点
   */
  const TouchPoint *getTouchById(int64 id) const;

  /**
   * @brief 检查是否有触摸
   */
  bool hasTouch() const { return touchCount() > 0; }

  /**
   * @brief 获取所有活跃触摸点
   */
  const std::vector<TouchPoint> &getTouches() const { return activeTouches_; }

  // ========== 游戏手柄 ==========

  /**
   * @brief 连接的游戏手柄数量
   */
  int32 gamepadCount() const;

  /**
   * @brief 检查手柄按键是否按下
   */
  bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const;

  /**
   * @brief 检查手柄按键是否刚按下
   */
  bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;

  /**
   * @brief 获取手柄轴值 (-1.0 到 1.0)
   */
  float getGamepadAxis(int32 idx, GamepadAxis axis) const;

  // ========== 回调设置 ==========

  /**
   * @brief 设置按键按下回调
   */
  void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); }

  /**
   * @brief 设置按键释放回调
   */
  void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); }

  /**
   * @brief 设置鼠标按下回调
   */
  void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }

  /**
   * @brief 设置鼠标释放回调
   */
  void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); }

  /**
   * @brief 设置触摸开始回调
   */
  void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); }

  /**
   * @brief 设置触摸移动回调
   */
  void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); }

  /**
   * @brief 设置触摸结束回调
   */
  void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); }

private:
  InputSvc() = default;

  static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES;
  static constexpr int32 MAX_GAMEPADS = 4;
  static constexpr int32 MAX_TOUCHES = 10;

  std::array<uint8, KEY_COUNT> keyState_{};
  std::array<uint8, KEY_COUNT> keyPrev_{};

  int32 mouseX_ = 0;
  int32 mouseY_ = 0;
  int32 mouseWheel_ = 0;
  std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mouseState_{};
  std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mousePrev_{};

  std::vector<TouchPoint> activeTouches_;
  std::vector<TouchPoint> endedTouches_;

  SDL_GameController *gamepads_[MAX_GAMEPADS] = {};
  std::array<uint8, static_cast<size_t>(GamepadBtn::Count)>
      padState_[MAX_GAMEPADS];
  std::array<uint8, static_cast<size_t>(GamepadBtn::Count)>
      padPrev_[MAX_GAMEPADS];

  KeyCb onKeyDown_;
  KeyCb onKeyUp_;
  MouseBtnCb onMouseDown_;
  MouseBtnCb onMouseUp_;
  TouchCb onTouchBegan_;
  TouchCb onTouchMoved_;
  TouchCb onTouchEnded_;

  void processEvent(const SDL_Event &evt);
  void openGamepad(int32 idx);
  void closeGamepad(int32 idx);

  void processTouchDown(const SDL_TouchFingerEvent &evt);
  void processTouchUp(const SDL_TouchFingerEvent &evt);
  void processTouchMotion(const SDL_TouchFingerEvent &evt);

  friend class WindowSvc;
};

#define INPUT_SVC extra2d::InputSvc::inst()

} // namespace extra2d
