// SPDX-License-Identifier: Unlicense

#include "ini.h"

#include "file.h"
#include "global.h"
#include "string_.h"

std::map<std::u8string, std::map<std::u8string, std::u8string>> Ini::loadIniContent(const char8_t* str, u64 strSize)
{
  std::map<std::u8string, std::map<std::u8string, std::u8string>> iniContent;

  i64 sectionBegin = -1;
  i64 keyBegin = -1;
  i64 equalPos = -1;
  std::u8string section;

  for (i64 i = 0; i < i64(strSize); ++i)
  {
    switch (str[i])
    {
    case '[':
      if (equalPos == -1) // values are allowed to contain '['
        sectionBegin = i;
      break;
    case ']':
      if (equalPos == -1) // values are allowed to contain ']'
      {
        ASSERT(sectionBegin >= 0);
        section.assign(&str[sectionBegin] + 1, i - sectionBegin - 1);
        iniContent[section];
      }
      break;
    case '=':
      if (equalPos == -1) // value are allowed to contain '=', e.g. base64 encoded data
        equalPos = i;
      break;
    case '\n':
      if (equalPos >= 0)
      {
        ASSERT(keyBegin >= 0);
        const std::u8string key(&str[keyBegin] + 1, equalPos - keyBegin - 1);
        const std::u8string value(&str[equalPos] + 1, i - equalPos - 1);
        iniContent[section][key] = value;
      }
      keyBegin = i;
      equalPos = -1;
      break;
    case '\0':
      ASSERT(i == i64(strSize) - 1);
      break;
    }
  }

  return iniContent;
}

std::map<std::u8string, std::vector<Ini::KeyValue>> Ini::loadIniContent_keepKeyOrder(const char8_t* str, u64 strSize)
{
  std::map<std::u8string, std::vector<Ini::KeyValue>> iniContent;

  i64 sectionBegin = -1;
  i64 keyBegin = -1;
  i64 equalPos = -1;
  std::u8string section;

  for (i64 i = 0; i < i64(strSize); ++i)
  {
    switch (str[i])
    {
    case u8'[':
      if (equalPos == -1) // values are allowed to contain '['
        sectionBegin = i;
      break;
    case u8']':
      if (equalPos == -1) // values are allowed to contain ']'
      {
        ASSERT(sectionBegin >= 0);
        section.assign(&str[sectionBegin] + 1, i - sectionBegin - 1);
      }
      break;
    case u8'=':
      if (equalPos == -1) // value are allowed to contain '=', e.g. base64 encoded data
        equalPos = i;
      break;
    case u8'\n':
      if (equalPos >= 0)
      {
        ASSERT(keyBegin >= 0);
        const std::u8string key(&str[keyBegin] + 1, equalPos - keyBegin - 1);
        const std::u8string value(&str[equalPos] + 1, i - equalPos - 1);
        iniContent[section].push_back({ key, value });
      }
      keyBegin = i;
      equalPos = -1;
      break;
    case u8'\0':
      ASSERT(i == i64(strSize) - 1);
      break;
    }
  }

  return iniContent;
}

void Ini::saveIniFile(const char8_t* filePath, const std::map<std::u8string, std::map<std::u8string, std::u8string>>& iniContent)
{
  if (Global::installMode != InstallMode::installed)
    return;

  FILE* file = File::fopen_u8(filePath, true);
  ASSERT(file != nullptr);

  for (const auto& [section, keyValue] : iniContent)
  {
    fprintf(file, "[%s]\n", reinterpret_cast<const char*>(section.c_str()));

    for (const auto& [key, value] : keyValue)
      fprintf(file, "%s=%s\n", reinterpret_cast<const char*>(key.c_str()), reinterpret_cast<const char*>(value.c_str()));
  }

  fclose(file);
}

void Ini::saveIniFile_keepSectionOrder(const char8_t* filePath, const std::vector<SectionKeyValue>& iniContent)
{
  if (Global::installMode != InstallMode::installed)
    return;

  FILE* file = File::fopen_u8(filePath, true);
  ASSERT(file != nullptr);

  for (const auto& [section, keyValue] : iniContent)
  {
    fprintf(file, "[%s]\n", reinterpret_cast<const char*>(section.c_str()));

    for (const auto& [key, value] : keyValue)
      fprintf(file, "%s=%s\n", reinterpret_cast<const char*>(key.c_str()), reinterpret_cast<const char*>(value.c_str()));
  }

  fclose(file);
}
