#pragma once

#include <maps/libs/common/include/exception.h>
#include <maps/libs/introspection/include/comparison.h>

#include <string>
#include <sstream>
#include <vector>

namespace maps::wiki::common {

struct LocalizedString
{
    std::string lang;
    std::string value;
};

inline auto introspect(const LocalizedString& lstring)
{
    return std::tie(lstring.lang, lstring.value);
};

using introspection::operator==;
using introspection::operator<;


/**
 * Joins range of objects, converting them to string by calling Func
 * Inserts delimiter between each couple of adjacent objects
 */
template <typename RangeIterator, typename Func, typename Delimiter>
std::string join(
    RangeIterator begin,
    RangeIterator end,
    Func func,
    const Delimiter& delimiter)
{
    std::ostringstream output;
    for (auto it = begin; it != end; ++it) {
        if (it != begin) {
            output << delimiter;
        }
        output << func(*it);
    }
    return output.str();
}

template <typename Container, typename Func, typename Delimiter>
std::string join(
    const Container& container,
    Func func,
    const Delimiter& delimiter)
{
    return join(container.begin(), container.end(), func, delimiter);
}

template <typename Container, typename Delimiter>
std::string join(
    const Container& container,
    const Delimiter& delimiter)
{
    using Value = typename Container::value_type;
    return join(container, [](const Value& value){ return value; }, delimiter);
}

template<typename OutString=std::string>
std::vector<OutString> split(const std::string_view source, const std::string_view delimeter)
{
    using TSize = std::string_view::size_type;
    REQUIRE(!delimeter.empty(), "Empty delimeter!");

    std::vector<OutString> result;
    for (TSize start = 0, end = 0; end != std::string_view::npos; start = end + delimeter.size()) {
        end = source.find(delimeter, start);

        const TSize size = end != std::string_view::npos
            ? end - start
            : std::string_view::npos;

        result.emplace_back(source.substr(start, size));
    }

    return result;
}

std::pair<std::string, std::string> splitKeyValue(std::string_view source, char delimeter = '=');

std::string_view stripQuotas(std::string_view source, char quotaChar = '"');

std::string escapeCData(const std::string& data);

std::string escapeForJSON(const std::string& text);

} // namespace maps::wiki::common
