#include <yandex/maps/wiki/common/string_utils.h>

namespace maps::wiki::common {
namespace {

const std::string CDATA_BEGIN = "<![CDATA[";
const std::string CDATA_END = "]]>";
const std::string CDATA_END_ESCAPED = "]]]]><![CDATA[>";

} // namespace

std::pair<std::string, std::string> splitKeyValue(std::string_view source, char delimeter)
{
    auto pos = source.find(delimeter);
    REQUIRE(pos != std::string_view::npos,
        "Delimeter '" << delimeter << "' is not found in the string '" << source << "'");
    REQUIRE(pos > 0, "Key has zero length in the string '" << source << "'");

    auto key = source.substr(0, pos);
    auto value = pos < source.size()
        ? source.substr(pos + 1, source.size() - pos - 1)
        : std::string_view();

    return std::pair<std::string, std::string>{key, value};
}

std::string_view stripQuotas(std::string_view source, char quotaChar)
{
    REQUIRE(source.size() >= 2, "Wrong source size " << source.size());
    REQUIRE(source[0] == quotaChar && source.back() == quotaChar,
        "Failed to find quotas in source '" << source << "'");
    return source.substr(1, source.size() - 2);
}


std::string escapeCData(const std::string& data)
{
    auto result = CDATA_BEGIN;
    size_t iPartBegin = 0;
    size_t iPartEnd = data.find(CDATA_END, iPartBegin);
    while (iPartEnd != std::string::npos) {
        auto text = data.substr(iPartBegin, iPartEnd - iPartBegin);
        for (auto& ch : text) {
            if (ch == '\x0b') {
                ch = '\n';
            }
        }
        result += text;
        result += CDATA_END_ESCAPED;

        iPartBegin = iPartEnd + CDATA_END.size();
        iPartEnd = data.find(CDATA_END, iPartBegin);
    }
    result += data.substr(iPartBegin);
    result += CDATA_END;
    return result;
}

std::string
escapeForJSON(const std::string& text) {
    const char *str = text.c_str();
    const char *end = str + text.length();
    std::string result;
    while (str < end) {
        char ch = *str;
        switch(ch) {
            case '"':
            case '/':
                result.push_back('\\');
                break;
            case '\b':
                result.push_back('\\');
                ch = 'b';
                break;
            case '\f':
                result.push_back('\\');
                ch = 'f';
                break;
            case '\n':
                result.push_back('\\');
                ch = 'n';
                break;
            case '\r':
                result.push_back('\\');
                ch = 'r';
                break;
            case '\t':
                result.push_back('\\');
                ch = 't';
                break;
            case '\\':
                result.push_back('\\');
                if (end - str > 5 && str[1] == 'u' &&
                    isxdigit(str[2]) && isxdigit(str[3]) &&
                    isxdigit(str[4]) && isxdigit(str[5]))
                {
                    result.append(str, 6);
                    str += 6;
                    continue;
                }
            default:
                if (ch >= 0 && ch < ' ')
                {
                    static const auto tohex = [](char c)
                    {
                        return char(c < 10 ? ('0' + c) : 'a' + (c - 10));
                    };
                    result.append({
                        '\\', 'u', '0', '0', tohex(*str / 16), tohex(*str % 16)});
                    str++;
                    continue;
                }
        }
        result.push_back(ch);
        ++str;
    }
    return result;
}

} // namespace maps::wiki::common
