#pragma once

#include "map/GameDataLoader.h"
#include "map/GameMapLayer.h"
#include <frostbite2D/2d/actor.h>
#include <frostbite2D/audio/music.h>
#include <unordered_map>

namespace frostbite2D {

/**
 * @brief 地图运行时容器，负责把 map 配置组织成可显示/可交互的场景结构
 *
 * 这里不直接解析底层资源格式，而是消费 `GameDataLoader` 已经整理好的
 * `MapConfig/TileInfo`。GameMap 主要负责三件事：
 * 1. 按固定图层顺序挂载地板、背景动画、场景动画和动态对象
 * 2. 根据 camera limit 和可移动区域维护摄像机关注点
 * 3. 提供移动判定和切图区域查询给上层角色/场景逻辑使用
 */
class GameMap : public Actor {
public:
  static constexpr size_t kInvalidMoveAreaIndex = static_cast<size_t>(-1);
  using MapMoveArea = game::MoveAreaTarget;

  GameMap();
  ~GameMap() override = default;

  /// 读取地图配置并重建当前地图内容。重复调用时会先清空旧地图运行态。
  bool LoadMap(const std::string& mapName);
  /// 地图进入场景后的初始化入口，目前主要负责音乐播放。
  void Enter();
  void Update(float deltaTime) override;
  void Render() override;

  /// 将运行时对象挂到 normal 层，并按 y 值设置基础排序。
  void AddObject(RefPtr<Actor> object);
  void AddObjectToLayer(const std::string& layerName, RefPtr<Actor> object);

  /// 返回地图推荐的默认相机关注点。
  Vec2 GetDefaultCameraFocus() const;
  /// 根据 camera limit 和缩放约束修正相机关注点。
  Vec2 ClampCameraFocus(const Vec2& focus, float zoom) const;
  /// 将各层转换到屏幕空间；远景层在这里做视差滚动。
  void ApplyCameraFocus(const Vec2& focus);

  /// 根据虚拟可行走区域限制位移，返回修正后的目标坐标。
  Vec3 CheckIsItMovable(const Vec3& curPos, const Vec3& posOffset) const;
  /// 检查当前位置是否进入 town move area，用于切图/传送判定。
  MapMoveArea CheckIsItMoveArea(const Vec3& curPos) const;
  bool TryGetMoveAreaTarget(const Vec3& curPos, MapMoveArea& outTarget) const;
  size_t FindMoveAreaIndex(const Vec3& curPos) const;
  const std::vector<MapMoveArea>& GetMoveAreaInfo() const;
  size_t GetMoveAreaCount() const { return moveArea_.size(); }
  game::MoveAreaBounds GetMovablePositionBounds(size_t index) const;
  Rect GetMovablePositionArea(size_t index) const;
  const std::string& GetMapPath() const { return mapConfig_.mapPath; }
  void SetDebugHighlightedMoveAreaIndex(size_t index);

  int GetBackgroundRepeatWidth() const { return backgroundRepeatWidth_; }
  bool IsDebugModeEnabled() const { return debugMode_; }
  bool IsMovableAreaCheckEnabled() const { return movableAreaCheckEnabled_; }

private:
  /// 初始化固定图层。图层名字与 DNF 地图层概念保持一致。
  void initLayers();
  /// 清理当前地图的运行态节点和缓存数据，但保留 GameMap 自身及图层骨架。
  void clearLayerChildren();
  /// 创建普通 tile 和扩展 tile，并推导背景平铺需要的横向覆盖宽度。
  void InitTile();
  /// 初始化背景动画层，必要时按横向覆盖宽度做横向平铺。
  void InitBackgroundAnimation();
  /// 初始化地图对象动画（门、特效、场景摆件等）。
  void InitMapAnimation();
  /// 初始化角色移动用的可行走区域。
  void InitVirtualMovableArea();
  /// 初始化切图/传送区域。
  void InitMoveArea();
  /// 将各层转换到屏幕空间；远景层在这里做视差滚动。
  void updateLayerPositions(const Vec2& cameraFocus);
  void PrepareRuntimeObjectsForRender();

  /// 原始地图配置，作为运行时装配地图内容的输入。
  game::MapConfig mapConfig_;
  /// 地图的固定图层集合，key 为地图层名。
  std::unordered_map<std::string, RefPtr<GameMapLayer>> layerMap_;
  /// bottom 层里的专用地板容器，固定放在该层最底部，避免地图动画被地板压住。
  RefPtr<Actor> tileRoot_;
  /// 角色可行走矩形区域。
  std::vector<Vec2> movablePolygon_;
  /// 进入后会触发切图/传送的矩形区域。
  std::vector<Rect> moveArea_;
  /// 地板铺设后推导出的横向覆盖宽度，用于背景动画横向平铺。
  int backgroundRepeatWidth_ = 0;
  /// 地图配置里的整体 Y 偏移；既影响层位置，也影响地板校准。
  int mapOffsetY_ = 0;
  bool debugMode_ = true;
  /// 硬编码调试开关：关闭后忽略可行走区域检测，允许角色自由移动。
  bool movableAreaCheckEnabled_ = false;
  /// 当前地图正在播放的背景音乐。
  Ptr<Music> currentMusic_;
};

} // namespace frostbite2D
