#pragma once

#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/resource/binary_reader.h>
#include <optional>
#include <string>
#include <map>
#include <vector>

namespace frostbite2D {

/**
 * @brief PVF 文件信息结构体
 */
struct PvfFileInfo {
  size_t offset = 0;       ///< 相对于数据起始位置的偏移
  uint32 crc32 = 0;         ///< CRC32 校验值
  size_t length = 0;         ///< 文件长度（字节）
  bool decoded = false;      ///< 是否已解码
};

/**
 * @brief 原始文件数据结构体
 */
struct RawData {
  std::unique_ptr<char[]> data;  ///< 原始数据指针（智能指针自动管理内存）
  size_t size;                    ///< 数据大小（字节）
};

/**
 * @brief PVF 资源包归档类
 *
 * 用于读取和解析 PVF 格式的游戏资源包文件。
 * 提供文件索引管理、字符串资源访问和文件内容读取功能。
 *
 * @example
 * auto& archive = PvfArchive::get();
 * if (archive.open("Script.pvf")) {
 *   archive.init();
 *   auto content = archive.getFileContent("script/example.txt");
 * }
 */
class PvfArchive {
public:
  /**
   * @brief 获取单例实例
   * @return PVF 归档实例引用
   */
  static PvfArchive& get();

  PvfArchive(const PvfArchive&) = delete;
  PvfArchive& operator=(const PvfArchive&) = delete;
  PvfArchive(PvfArchive&&) = delete;
  PvfArchive& operator=(PvfArchive&&) = delete;

  // ---------------------------------------------------------------------------
  // 文件操作
  // ---------------------------------------------------------------------------

  /**
   * @brief 打开 PVF 文件
   * @param filePath 文件路径
   * @return 打开成功返回 true
   */
  bool open(const std::string& filePath = "Script.pvf");

  /**
   * @brief 关闭并清空所有数据
   */
  void close();

  /**
   * @brief 检查是否已打开
   * @return 已打开返回 true
   */
  bool isOpen() const;

  // ---------------------------------------------------------------------------
  // 初始化
  // ---------------------------------------------------------------------------

  /**
   * @brief 完整初始化（解析头部、加载字符串表）
   */
  void init();

  /**
   * @brief 解析 PVF 文件头部并建立文件索引
   */
  void initHeader();

  /**
   * @brief 加载二进制字符串表 (stringtable.bin)
   */
  void initBinStringTable();

  /**
   * @brief 加载本地化字符串 (n_string.lst)
   */
  void initLoadStrings();

  // ---------------------------------------------------------------------------
  // 文件信息查询
  // ---------------------------------------------------------------------------

  /**
   * @brief 检查文件是否存在
   * @param path 文件路径
   * @return 存在返回 true
   */
  bool hasFile(const std::string& path) const;

  /**
   * @brief 获取文件信息
   * @param path 文件路径
   * @return 文件信息，不存在返回 std::nullopt
   */
  std::optional<PvfFileInfo> getFileInfo(const std::string& path) const;

  /**
   * @brief 获取所有文件路径列表
   * @return 文件路径列表
   */
  std::vector<std::string> listFiles() const;

  // ---------------------------------------------------------------------------
  // 文件内容读取
  // ---------------------------------------------------------------------------

  /**
   * @brief 获取文件内容作为字符串
   * @param path 文件路径
   * @return 文件内容，失败返回 std::nullopt
   */
  std::optional<std::string> getFileContent(const std::string& path);

  /**
   * @brief 获取文件内容作为字节数组
   * @param path 文件路径
   * @return 字节数组，失败返回 std::nullopt
   */
  std::optional<std::vector<uint8>> getFileBytes(const std::string& path);

  /**
   * @brief 获取文件原始数据（使用智能指针管理内存，已包含 CRC 解密）
   *
   * 与原始 GetFileContentChar 功能相同，但使用智能指针避免内存泄漏。
   * 数据会在首次访问时自动进行 CRC 解密。
   *
   * @param path 文件路径
   * @return RawData 结构体，包含数据指针和大小；失败返回 std::nullopt
   */
  std::optional<RawData> getFileRawData(const std::string& path);

  // ---------------------------------------------------------------------------
  // 字符串资源访问
  // ---------------------------------------------------------------------------

  /**
   * @brief 获取二进制字符串
   * @param key 字符串键
   * @return 字符串，不存在返回 std::nullopt
   */
  std::optional<std::string> getBinString(int key) const;

  /**
   * @brief 获取本地化字符串
   * @param type 字符串类型
   * @param key 字符串键
   * @return 字符串，不存在返回 std::nullopt
   */
  std::optional<std::string> getLoadString(const std::string& type, const std::string& key) const;

  /**
   * @brief 检查二进制字符串是否存在
   * @param key 字符串键
   * @return 存在返回 true
   */
  bool hasBinString(int key) const;

  /**
   * @brief 检查本地化字符串是否存在
   * @param type 字符串类型
   * @param key 字符串键
   * @return 存在返回 true
   */
  bool hasLoadString(const std::string& type, const std::string& key) const;

private:
  PvfArchive() = default;
  ~PvfArchive() = default;

  /**
   * @brief 规范化路径（转为小写）
   * @param path 原始路径
   * @return 规范化后的路径
   */
  std::string normalizePath(const std::string& path) const;

  /**
   * @brief 分割字符串
   * @param str 要分割的字符串
   * @param delimiter 分隔符
   * @return 分割后的字符串列表
   */
  std::vector<std::string> splitString(const std::string& str, const std::string& delimiter) const;

  /**
   * @brief 解码文件内容（内部使用）
   * @param info 文件信息（会修改 decoded 标志）
   * @return 成功返回 true
   */
  bool decodeFile(PvfFileInfo& info);

  BinaryReader reader_;                           ///< 二进制读取器
  size_t dataStartPos_ = 0;                       ///< 数据起始位置
  std::map<std::string, PvfFileInfo> fileInfo_;  ///< 文件信息映射
  std::map<int, std::string> binStringTable_;    ///< 二进制字符串表
  std::map<std::string, std::map<std::string, std::string>> loadStrings_;  ///< 本地化字符串
};

} // namespace frostbite2D
