#include <frostbite2D/resource/script_parser.h>
#include <frostbite2D/resource/pvf_archive.h>
#include <algorithm>
#include <cctype>
#include <SDL.h>

namespace frostbite2D {

std::string ScriptValue::toString() const {
  switch (type) {
    case ScriptValueType::Integer:
      return std::to_string(intValue);
    case ScriptValueType::Float:
      return std::to_string(floatValue);
    case ScriptValueType::String:
      return stringValue;
    case ScriptValueType::Empty:
    default:
      return "";
  }
}

ScriptParser::ScriptParser(const RawData& data, const std::string& filePath)
  : filePath_(filePath)
  , fileType_(extractFileType(filePath)) {
  if (data.data && data.size > 0) {
    data_.resize(data.size);
    std::memcpy(data_.data(), data.data.get(), data.size);
  }
  if (data_.size() < 7) {
    data_.clear();
  }
}

ScriptParser::ScriptParser(const std::vector<uint8>& data, const std::string& filePath)
  : filePath_(filePath)
  , fileType_(extractFileType(filePath)) {
  if (data.size() > 0) {
    data_.resize(data.size());
    std::memcpy(data_.data(), data.data(), data.size());
  }
  if (data_.size() < 7) {
    data_.clear();
  }
}

ScriptParser::ScriptParser(const char* data, size_t size, const std::string& filePath)
  : filePath_(filePath)
  , fileType_(extractFileType(filePath)) {
  if (data && size > 0) {
    data_.resize(size);
    std::memcpy(data_.data(), data, size);
  }
  if (data_.size() < 7) {
    data_.clear();
  }
}

std::optional<ScriptValue> ScriptParser::next() {
  if (!isValid() || isEnd()) {
    return std::nullopt;
  }

  auto value = parseValueAt(position_);
  if (value.has_value()) {
    position_ += 5;
  }
  return value;
}

void ScriptParser::back() {
  if (position_ >= 7) {
    position_ -= 5;
  }
}

bool ScriptParser::isEnd() const {
  return !isValid() || position_ >= data_.size() || (data_.size() - position_) < 5;
}

void ScriptParser::reset() {
  position_ = 2;
}

std::vector<ScriptValue> ScriptParser::parseAll() {
  std::vector<ScriptValue> values;
  reset();

  while (!isEnd()) {
    if (auto value = next()) {
      values.push_back(*value);
    }
  }

  return values;
}

size_t ScriptParser::position() const {
  return position_;
}

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

const std::string& ScriptParser::filePath() const {
  return filePath_;
}

const std::string& ScriptParser::fileType() const {
  return fileType_;
}

bool ScriptParser::isValid() const {
  return !data_.empty();
}

std::string ScriptParser::extractFileType(const std::string& filePath) const {
  size_t slashPos = filePath.find_first_of("/");
  if (slashPos == std::string::npos) {
    return "";
  }

  std::string type = filePath.substr(0, slashPos);
  std::transform(type.begin(), type.end(), type.begin(),
                 [](unsigned char c) { return std::tolower(c); });
  return type;
}

uint8 ScriptParser::readByte(size_t offset) const {
  if (offset >= data_.size()) {
    return 0;
  }
  return static_cast<uint8>(data_[offset]);
}

int32 ScriptParser::readInt32(size_t offset) const {
  if (offset + 4 > data_.size()) {
    return 0;
  }
  int32 value;
  std::memcpy(&value, data_.data() + offset, sizeof(int32));
  return value;
}

std::optional<ScriptValue> ScriptParser::parseValueAt(size_t offset) const {
  if (offset + 5 > data_.size()) {
    return std::nullopt;
  }

  ScriptValue result;
  PvfArchive& archive = PvfArchive::get();

  uint8 opcode = readByte(offset);
  int32 data = readInt32(offset + 1);

  switch (static_cast<ScriptOpcode>(opcode)) {
    case ScriptOpcode::Integer: {
      result.type = ScriptValueType::Integer;
      result.intValue = data;
      break;
    }

    case ScriptOpcode::Float: {
      result.type = ScriptValueType::Float;
      float floatValue;
      std::memcpy(&floatValue, &data, sizeof(float));
      result.floatValue = floatValue;
      break;
    }

    case ScriptOpcode::StringRef5:
    case ScriptOpcode::StringRef6:
    case ScriptOpcode::StringRef7:
    case ScriptOpcode::StringRef8: {
      result.type = ScriptValueType::String;
      if (auto str = archive.getBinString(data)) {
        result.stringValue = *str;
      }
      break;
    }

    case ScriptOpcode::ExtendedString9: {
      result.type = ScriptValueType::String;
      if (offset + 10 <= data_.size()) {
        uint8 newOpcode = readByte(offset + 5);
        (void)newOpcode;
        int32 newData = readInt32(offset + 6);

        if (auto binStr = archive.getBinString(newData)) {
          if (!binStr->empty()) {
            if (auto loadStr = archive.getLoadString(fileType_, *binStr)) {
              result.stringValue = *loadStr;
            }
          }
        }
      }
      break;
    }

    case ScriptOpcode::ExtendedString10: {
      result.type = ScriptValueType::String;
      if (auto binStr = archive.getBinString(data)) {
        if (!binStr->empty()) {
          if (auto loadStr = archive.getLoadString(fileType_, *binStr)) {
            result.stringValue = *loadStr;
          }
        }
      }
      break;
    }

    default: {
      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ScriptParser: 未知的操作码: %d", opcode);
      return std::nullopt;
    }
  }

  return result;
}

} // namespace frostbite2D
