#include <frostbite2D/resource/binary_reader.h>
#include <frostbite2D/resource/asset.h>
#include <fstream>
#include <SDL.h>

namespace frostbite2D {

BinaryReader::BinaryReader(const std::string& filePath) {
  open(filePath);
}

BinaryReader::BinaryReader(const char* data, size_t size) {
  loadFromMemory(data, size);
}

bool BinaryReader::open(const std::string& filePath) {
  close();

  Asset& asset = Asset::get();
  auto dataOpt = asset.readFileToBytes(filePath);

  if (!dataOpt.has_value()) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BinaryReader: 无法打开文件: %s", filePath.c_str());
    return false;
  }

  const std::vector<uint8>& bytes = dataOpt.value();
  data_.resize(bytes.size());
  std::memcpy(data_.data(), bytes.data(), bytes.size());

  position_ = 0;
  lastReadCount_ = 0;
  return true;
}

void BinaryReader::loadFromMemory(const char* data, size_t size) {
  close();

  if (data && size > 0) {
    data_.resize(size);
    std::memcpy(data_.data(), data, size);
  }

  position_ = 0;
  lastReadCount_ = 0;
}

void BinaryReader::close() {
  data_.clear();
  position_ = 0;
  lastReadCount_ = 0;
}

bool BinaryReader::isOpen() const {
  return !data_.empty();
}

size_t BinaryReader::tell() const {
  return position_;
}

void BinaryReader::seek(size_t pos) {
  position_ = std::clamp(pos, static_cast<size_t>(0), data_.size());
}

void BinaryReader::skip(size_t count) {
  seek(position_ + count);
}

bool BinaryReader::eof() const {
  return position_ >= data_.size();
}

size_t BinaryReader::size() const {
  return data_.size();
}

size_t BinaryReader::remaining() const {
  if (position_ >= data_.size()) {
    return 0;
  }
  return data_.size() - position_;
}

size_t BinaryReader::lastReadCount() const {
  return lastReadCount_;
}

size_t BinaryReader::read(char* buffer, size_t size) {
  if (!buffer || size == 0 || eof()) {
    lastReadCount_ = 0;
    return 0;
  }

  size_t bytesToRead = std::min(size, remaining());
  std::memcpy(buffer, data_.data() + position_, bytesToRead);
  position_ += bytesToRead;
  lastReadCount_ = bytesToRead;

  if (bytesToRead != size) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BinaryReader: 读取数据不完整，期望 %zu 字节，实际读取 %zu 字节", size, bytesToRead);
  }

  return bytesToRead;
}

std::vector<uint8> BinaryReader::readBytes(size_t size) {
  std::vector<uint8> result;
  size_t bytesToRead = std::min(size, remaining());

  if (bytesToRead > 0) {
    result.resize(bytesToRead);
    read(reinterpret_cast<char*>(result.data()), bytesToRead);
  }

  return result;
}

const char* BinaryReader::data() const {
  return data_.data();
}

const char* BinaryReader::currentData() const {
  if (eof()) {
    return nullptr;
  }
  return data_.data() + position_;
}

int8 BinaryReader::readInt8() {
  return read<int8>();
}

int16 BinaryReader::readInt16() {
  return read<int16>();
}

int32 BinaryReader::readInt32() {
  return read<int32>();
}

int64 BinaryReader::readInt64() {
  return read<int64>();
}

uint8 BinaryReader::readUInt8() {
  return read<uint8>();
}

uint16 BinaryReader::readUInt16() {
  return read<uint16>();
}

uint32 BinaryReader::readUInt32() {
  return read<uint32>();
}

uint64 BinaryReader::readUInt64() {
  return read<uint64>();
}

float BinaryReader::readFloat() {
  return read<float>();
}

double BinaryReader::readDouble() {
  return read<double>();
}

std::string BinaryReader::readString(size_t length) {
  if (length == 0 || eof()) {
    return "";
  }

  size_t bytesToRead = std::min(length, remaining());
  std::string result(data_.data() + position_, bytesToRead);
  position_ += bytesToRead;
  lastReadCount_ = bytesToRead;

  if (bytesToRead != length) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BinaryReader: 读取字符串不完整，期望 %zu 字节，实际读取 %zu 字节", length, bytesToRead);
  }

  return result;
}

std::string BinaryReader::readNullTerminatedString() {
  if (eof()) {
    return "";
  }

  size_t startPos = position_;
  while (position_ < data_.size() && data_[position_] != '\0') {
    ++position_;
  }

  size_t length = position_ - startPos;
  std::string result(data_.data() + startPos, length);

  if (position_ < data_.size()) {
    ++position_;
  }

  lastReadCount_ = length + 1;
  return result;
}

void BinaryReader::crcDecode(size_t length, uint32 crc32) {
  if (length == 0 || eof()) {
    return;
  }

  const uint32 key = 0x81A79011;
  size_t originalPos = position_;
  size_t bytesToProcess = std::min(length, remaining());

  for (size_t i = 0; i < bytesToProcess; i += 4) {
    size_t pos = position_;
    uint32 value = read<uint32>();

    uint32 decoded = (value ^ key ^ crc32);
    decoded = (decoded >> 6) | ((decoded << (32 - 6)) & 0xFFFFFFFF);

    if (pos + 3 < data_.size()) {
      data_[pos] = static_cast<char>((decoded >> 0) & 0xFF);
      data_[pos + 1] = static_cast<char>((decoded >> 8) & 0xFF);
      data_[pos + 2] = static_cast<char>((decoded >> 16) & 0xFF);
      data_[pos + 3] = static_cast<char>((decoded >> 24) & 0xFF);
    }
  }

  seek(originalPos + bytesToProcess);
}

} // namespace frostbite2D
