#include "string_helpers.h"

#include "helpers.h"

#include <algorithm>

namespace maps::wiki::flat_range {
namespace {

const char LIST_DELIMITER = ',';
const char MINUS = '-';
const char QUOTE = '"';

bool
isMinusOrDigit(char c)
{
    return c == MINUS || std::isdigit(c);
}

/* Function to find common begining of two char ranges but discarding trailing digits */
template<class CharIter>
size_t
commonNotNumberPartLen(CharIter b1, CharIter e1, CharIter b2, CharIter e2)
{
    size_t r = 0;
    CharIter it1 = b1;
    CharIter it2 = b2;
    size_t digitsInRow = 0;
    while (it1 != e1 && it2 != e2 && *it1 == *it2) {
        if (isMinusOrDigit(*it1)) {
            ++digitsInRow;
        } else {
            digitsInRow = 0;
        }
        ++r;
        ++it1;
        ++it2;
    }
    return r - digitsInRow;
};

void
addPart(std::vector<std::string>& parts, const std::string& part)
{
    if (part.empty()) {
        return;
    }
    if (parts.empty()) {
        parts.emplace_back(part);
    } else if (parts.back().back() == MINUS || part.front() == MINUS) {
        parts.back() = parts.back() + part;
    } else {
        parts.emplace_back(part);
    }
}

} // namespace

bool isNumber(const std::string& text)
{
    return !text.empty() && std::all_of(text.begin(), text.end(),
        [](unsigned char c) { return isMinusOrDigit(c); }
    );
}

void
replaceMinusWithBoundsDelimiterKeepNegative(std::string &s)
{
    CHECK(std::count_if(s.begin(), s.end(),
        [](const auto c) {
            return c == MINUS;
        }) < 4,
        "Malformed range description: " << s);
    for (size_t i = 1; i < s.size(); ++i) {
        if (s[i] == MINUS && s[i - 1] != BOUNDS_DELIMITER[0]) {
            s[i] = BOUNDS_DELIMITER[0];
            break;
        }
    }
    CHECK(std::count_if(s.begin(), s.end(),
        [](const auto c) {
            return c == MINUS;
        }) < 3,
        "Malformed range description: " << s);
    for (size_t i = 0; i < s.size(); ++i) {
        CHECK(s[i] != MINUS || (i < s.size() - 1 && std::isdigit(s[i + 1])),
            "Malformed range description: " << s);
    }
}

bool isQuoted(const std::string& text)
{
    return text.size() > 2 && text.front() == QUOTE && text.back() == QUOTE;
}


std::vector<std::string>
splitQuoted(const std::string& input)
{
    const auto trimmed = trim_copy(input);
    if (trimmed.empty()) {
        return {};
    }
    bool insideQuotes = false;
    std::vector<std::string> parts;
    std::string curPart;
    char prevChar = 0;
    for (const auto& c : trimmed) {
        if (c == QUOTE) {
            insideQuotes = !insideQuotes;
            CHECK_QUOTED(insideQuotes || !curPart.empty(), input);
            curPart += c;
        } else if (!insideQuotes && (c == LIST_DELIMITER || c == ' ')) {
            if (!curPart.empty()) {
                addPart(parts, curPart);
                curPart.clear();
            }
        } else {
            CHECK_QUOTED(insideQuotes || prevChar != QUOTE, input);
            curPart += c;
        }
        prevChar = c;
    }
    addPart(parts, curPart);

    if (parts.empty()) {
        parts.emplace_back(trimmed);
    }
    CHECK_QUOTED(!insideQuotes, input);
    return parts;
}

size_t commonNotNumberSuffixLen(const std::string& s1, const std::string& s2)
{
    return commonNotNumberPartLen(s1.rbegin(), s1.rend(), s2.rbegin(), s2.rend());
}

size_t commonNotNumberPrefixLen(const std::string& s1, const std::string& s2)
{
    return commonNotNumberPartLen(s1.begin(), s1.end(), s2.begin(), s2.end());
}

} // namespace maps::wiki::flat_range
