#include <set>
#include <regex>
#include <sstream>

#include "argtype.h"
#include "error.h"
#include "util.h"

Argtype::Argtype(std::string name, std::string desc, std::string symbolic_pattern, std::string pattern,
                 std::string python_cast, modify_string_f python_format,
                 const Locator& loc)
    : m_name(name)
    , m_desc(desc)
    , m_symbolic_pattern(symbolic_pattern)
    , m_pattern(pattern)
    , m_python_cast(python_cast)
    , m_python_format(python_format)
    , m_regex(std::regex(pattern))
    , m_loc(loc)
{
}

std::string pyformat_string(const strings_t& v)
{
    std::string p = v[0];
    std::string delim = "\"";
    if (contains(p, "\"\"\"")) {
        delim = "'''";
    } else if (contains(p, "'''")) {
        delim = "\"\"\"";
    } else if (contains(p, "\"")) {
        delim = "'";
    }
    return delim + p + delim;
}

std::string pyformat_bool(const strings_t& v)
{
    std::string value = v[0];
    std::set<std::string> true_values { "1", "true", "True", "yes" };
    std::set<std::string> false_values { "0", "false", "False", "no" };
    if (true_values.contains(value)) {
        return "True";
    } else if (false_values.contains(value)) {
        return "False";
    } else {
        throw Argument_error("The argument\"" + value + "\" is not a Boolean value");
    }
}

std::string pyformat_number(const strings_t& value)
{
    return value[0];
}

std::string pylist(strings_t words)
{
    std::transform(words.begin(), words.end(), words.begin(),
                   [](const std::string& s) { return pyformat_string({s}); });
    return "[" + join(words, ", ") + "]";
}

std::string pyformat_list(const strings_t& value)
{
    return pylist(value);
}

std::string pyformat_dlist(const strings_t& value)
{
    return pylist(value);
}

std::string Argtype::python_value(const std::string& var_name, std::vector<std::string> value, size_t name_size)
{
    std::stringstream ss {};
    ss << "    " << std::left << std::setw(name_size) << var_name << " = ";
    if (m_python_format) {
        ss << m_python_format(value);
    } else {
        ss << m_python_cast << "(" << pyformat_string(value) << ")";
    }
    return ss.str();
}

