// SPDX-License-Identifier: Unlicense

#include "string_.h"

#ifdef PLATFORM_WINDOWS
#include <Windows.h>
#endif // PLATFORM_WINDOWS
#include <charconv>

#ifdef PLATFORM_WINDOWS
std::wstring String::s2ws(const char* str, const i32 strSize)
{
  const int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<const char*>(str), strSize, nullptr, 0);
  std::wstring wstr(sizeNeeded, 0);
  MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<const char*>(str), strSize, wstr.data(), sizeNeeded);
  return wstr;
}

std::string String::ws2s(const wchar_t* str, i32 strSize)
{
  const int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, str, strSize, nullptr, 0, nullptr, nullptr);
  std::string u8str(sizeNeeded, 0);
  WideCharToMultiByte(CP_UTF8, 0, str, strSize, reinterpret_cast<char*>(u8str.data()), sizeNeeded, nullptr, nullptr);
  return u8str;
}

std::wstring String::ws_ExpandEnvironmentStrings(const std::wstring& wstr)
{
  if (wstr.find(L'%') == std::wstring::npos)
    return wstr;

  const DWORD sizeNeeded = ExpandEnvironmentStringsW(wstr.c_str(), nullptr, 0);
  std::wstring wstrExpanded(sizeNeeded - 1, 0);
  ExpandEnvironmentStringsW(wstr.c_str(), wstrExpanded.data(), sizeNeeded);
  //for (size_t i = 0; i < wstrExpanded.size(); ++i)
  //  if (wstrExpanded[i] == '\\')
  //    wstrExpanded[i] = '/';
  return wstrExpanded;
}
#endif // PLATFORM_WINDOWS

std::vector<std::string> String::split(const std::string& str, const char delimiter)
{
  std::vector<std::string> elems;

  if (str.empty())
    return elems;

  size_t offsetPrev = 0;
  for (size_t i = 0; i < str.size(); ++i)
  {
    if (str[i] == delimiter)
    {
      elems.emplace_back(str.data() + offsetPrev, i - offsetPrev);
      offsetPrev = i + 1;
    }
  }

  elems.emplace_back(str.data() + offsetPrev, str.size() - offsetPrev);

  return elems;
}

//i32 String::utf8CharCount(const char* utf8String)
//{
//  i32 charCount = 0;
//
//  i32 i = 0;
//  while (utf8String[i] != '\0')
//  {
//    // Determine the width of the UTF-8 character
//    if ((utf8String[0] & 0b10000000) == 0)
//    {
//      // Single-byte character
//      i += 1;
//    }
//    else if ((utf8String[0] & 0b11100000) == 0b11000000)
//    {
//      // Two-byte character
//      ASSERT((utf8String[1] & 0b11000000) == 0b10000000);
//      i += 2;
//    }
//    else if ((utf8String[0] & 0b11110000) == 0b11100000)
//    {
//      // Three-byte character
//      ASSERT((utf8String[1] & 0b11000000) == 0b10000000);
//      ASSERT((utf8String[2] & 0b11000000) == 0b10000000);
//      i += 3;
//    }
//    else if ((utf8String[0] & 0b11111000) == 0b11110000)
//    {
//      // Four-byte character
//      ASSERT((utf8String[1] & 0b11000000) == 0b10000000);
//      ASSERT((utf8String[2] & 0b11000000) == 0b10000000);
//      ASSERT((utf8String[3] & 0b11000000) == 0b10000000);
//      i += 4;
//    }
//    ++charCount;
//  }
//
//  return charCount;
//}

char32_t String::utf32Char(const char* utf8String, i8& utf8CharBytes)
{
  // Determine the width of the UTF-8 character
  if ((utf8String[0] & 0b10000000) == 0)
  {
    // Single-byte character
    utf8CharBytes = 1;
    return static_cast<char32_t>(utf8String[0]);
  }

  if ((utf8String[0] & 0b11100000) == 0b11000000)
  {
    // Two-byte character
    ASSERT((utf8String[1] & 0b11000000) == 0b10000000);
    utf8CharBytes = 2;
    return static_cast<char32_t>(utf8String[0] & 0b00011111) << 6
      | (utf8String[1] & 0b00111111);
  }

  if ((utf8String[0] & 0b11110000) == 0b11100000)
  {
    // Three-byte character
    ASSERT((utf8String[1] & 0b11000000) == 0b10000000);
    ASSERT((utf8String[2] & 0b11000000) == 0b10000000);
    utf8CharBytes = 3;
    return static_cast<char32_t>(utf8String[0] & 0b00011111) << 12
      | static_cast<char32_t>(utf8String[1] & 0b00011111) << 6
      | (utf8String[2] & 0b00111111);
  }

  if ((utf8String[0] & 0b11111000) == 0b11110000)
  {
    // Four-byte character
    ASSERT((utf8String[1] & 0b11000000) == 0b10000000);
    ASSERT((utf8String[2] & 0b11000000) == 0b10000000);
    ASSERT((utf8String[3] & 0b11000000) == 0b10000000);
    utf8CharBytes = 4;
    return static_cast<char32_t>(utf8String[0] & 0b00011111) << 18
      | static_cast<char32_t>(utf8String[1] & 0b00011111) << 12
      | static_cast<char32_t>(utf8String[2] & 0b00011111) << 6
      | (utf8String[3] & 0b00111111);
  }

  ASSERT(false);
  return U'\0';
}

//std::string string::join(const std::vector<std::string>& vec, const char delimeter)
//{
//  std::stringstream res;
//  std::copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(res, &delimeter));
//  return res.str();
//}

//bool string::endsWith(const std::string& str, const std::string& ending)
//{
//  if (str.length() >= ending.length())
//    return (0 == str.compare(str.length() - ending.length(), ending.length(), ending));
//  return false;
//}

i32 atoi2(const char* str)
{
  return atoi(reinterpret_cast<const char*>(str));
}
u64 atou64(const char* str, const u64 size)
{
  u64 value;
  std::from_chars(reinterpret_cast<const char*>(str), reinterpret_cast<const char*>(str) + size, value);
  return value;
}
f64 atof2(const char* str)
{
  return atof(reinterpret_cast<const char*>(str));
}
std::string to_string(const bool value)
{
  return value ? "1" : "0";
}
std::string to_string(const u8 value)
{
  const i32 len = snprintf(nullptr, 0, "%hhu", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%hhu", value);
  return str;
}
std::string to_string(const i8 value)
{
  const i32 len = snprintf(nullptr, 0, "%hhd", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%hhd", value);
  return str;
}
std::string to_string(const u16 value)
{
  const i32 len = snprintf(nullptr, 0, "%hu", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%hu", value);
  return str;
}
std::string to_string(const i16 value)
{
  const i32 len = snprintf(nullptr, 0, "%hd", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%hd", value);
  return str;
}
std::string to_string(const u32 value)
{
  const i32 len = snprintf(nullptr, 0,"%u", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%u", value);
  return str;
}
std::string to_string(const i32 value)
{
  const i32 len = snprintf(nullptr, 0,"%d", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%d", value);
  return str;
}
std::string to_string(const u64 value)
{
  const i32 len = snprintf(nullptr, 0,"%llu", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%llu", value);
  return str;
}
std::string to_string(const i64 value)
{
  const i32 len = snprintf(nullptr, 0,"%lld", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%lld", value);
  return str;
}
std::string to_string(const f32 value)
{
  const i32 len = snprintf(nullptr, 0,"%g", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%g", value);
  return str;
}
std::string to_string(const f64 value)
{
  const i32 len = snprintf(nullptr, 0,"%lg", value);
  std::string str(len, '\0');
  sprintf(reinterpret_cast<char*>(str.data()), "%lg", value);
  return str;
}
