#include "strings.hpp"

#include "../log.hpp"

#include <format>
#include <tinyxml2/tinyxml2.h>

using namespace tinyxml2;

namespace game::resource::xml
{
  namespace
  {
    XMLError document_load(const util::physfs::Path& path, XMLDocument& document)
    {
      if (!path.is_valid())
      {
        logger.error(std::format("Failed to open XML document: {} ({})", path.c_str(), util::physfs::error_get()));
        return XML_ERROR_FILE_NOT_FOUND;
      }

      auto buffer = path.read();
      if (buffer.empty())
      {
        logger.error(std::format("Failed to read XML document: {} ({})", path.c_str(), util::physfs::error_get()));
        return XML_ERROR_FILE_COULD_NOT_BE_OPENED;
      }

      auto result = document.Parse((const char*)buffer.data(), buffer.size());
      if (result != XML_SUCCESS)
        logger.error(std::format("Failed to parse XML document: {} ({})", path.c_str(), document.ErrorStr()));

      return result;
    }

    XMLError query_string_attribute(XMLElement* element, const char* attribute, std::string* value)
    {
      const char* temp = nullptr;
      auto result = element->QueryStringAttribute(attribute, &temp);
      if (result == XML_SUCCESS && temp && value) *value = temp;
      return result;
    }

    std::string definition_element_name_get(const Strings::Definition& definition)
    {
      std::string name = definition.attribute;
      if (name.rfind("Text", 0) == 0) name.replace(0, 4, "String");
      return name;
    }
  }

  Strings::Strings()
  {
    for (int i = 0; i < Count; i++)
      values[i] = definitions[i].fallback;
  }

  Strings::Strings(const util::physfs::Path& path)
  {
    for (int i = 0; i < Count; i++)
      values[i] = definitions[i].fallback;

    XMLDocument document;
    if (document_load(path, document) != XML_SUCCESS)
    {
      logger.error(std::format("Unable to initialize strings: {} ({})", path.c_str(), document.ErrorStr()));
      return;
    }

    auto root = document.RootElement();
    if (!root) return;

    for (int i = 0; i < Count; i++)
      if (auto element = root->FirstChildElement(definition_element_name_get(definitions[i]).c_str()))
        query_string_attribute(element, "Text", &values[i]);

    isValid = true;
    logger.info(std::format("Initialized strings: {}", path.c_str()));
  }

  const std::string& Strings::get(Type type) const { return values[type]; }
}
