#include <regex>
#include <sstream>
#include <algorithm>
#include <numeric>

#include "argtype_registry.h"
#include "error.h"
#include "show.h"
#include "log.h"
#include "util.h"
#include "character.h"
#include "katom.h"

Parameter_set& Argtype_registry::parameters()
{
    static Parameter_set instance("name | desc :pattern .* :python_cast str :default :alone");
    return instance;
}

Argtype_registry::Argtype_registry()
{
    (void)(void)K::log(2);
    Locator loc = current_locator();
    for (auto [name, desc, pattern, python_cast, python_format] : base_argtypes) {
        add(name, desc, pattern, "", "", python_cast, python_format, loc);
    }
    // rest is a parameterized type (rest(N)); an unparameterized use is
    // one-dimensional.
    m_types["rest"].m_parameter = "1";
    // A bool option written alone is true: :number means :number true.
    // This is a declaration like any other type's :alone, not an engine
    // special case for booleans.
    m_types["bool"].m_alone = "true";
}

std::string Argtype_registry::replace_symbols(const std::string& pattern, const Locator& loc)
{
    std::smatch match {};
    std::regex symbol_pat(R"('(\w+)')");
    std::string expanded { pattern };
    for (const std::string& symbol : find_all(pattern, symbol_pat, 0)) {
        std::string name { symbol.begin()+1, symbol.end()-1 };
        if (m_types.find(name) != m_types.end()) {
            expanded = string_replace(
                expanded, symbol, R"((?:)" + m_types[name].m_pattern + R"())");
        } else {
            std::stringstream ss;
            ss << "Argtype symbol " << symbol << " not defined.\n\n"
               << "Defined argtypes:\n";
            ss << describe();
            throw Definition_error(ss.str(), loc);
        }
    }
    return expanded;
}

void Argtype_registry::check_for_existing_definition(
    const std::string& name, const Locator& loc)
{
    if (count(m_names.begin(), m_names.end(), name) > 0) {
        std::stringstream ss {};
        ss << "Argument type '" << name << "' is already defined at "
           << m_types[name].m_loc;
        throw Definition_error(ss.str(), loc);
    }
}

void Argtype_registry::add(const std::string& name, const std::string& desc,
                      const std::string& pattern, const std::string& default_value,
                      const std::string& alone_value,
                      const std::string& python_cast, modify_string_f python_format,
                      const Locator& loc)
{
    // (void)K::log(3, name, ":", abbrev(string_replace(desc, "\n", "/"), 50), pattern);
    check_for_existing_definition(name, loc);
    std::string expanded_pattern = replace_symbols(pattern, loc);
    m_name_size = std::max(m_name_size, name.size()); // For display
    m_pattern_size = std::max(m_pattern_size, expanded_pattern.size());
    try {
        m_types[name] = Argtype(name, desc, pattern, expanded_pattern,
                                default_value, alone_value,
                                python_cast, python_format, loc);
    } catch (const std::regex_error& e) {
        std::stringstream ss {};
        ss << "The pattern for argument type \"" << name
           << "\" is not a valid regular expression (" << e.what() << "):\n"
           << "  " << pattern << "\n";
        if (pattern != expanded_pattern) {
            ss << "expanded to:\n  " << expanded_pattern << "\n";
        }
        throw Definition_error(ss.str(), loc);
    }
    if (!default_value.empty() &&
        !std::regex_match(default_value, m_types[name].m_regex)) {
        std::stringstream ss {};
        ss << "The default value \"" << default_value << "\" for argument type \""
           << name << "\" does not match its own pattern:\n"
           << "  " << pattern << "\n";
        throw Definition_error(ss.str(), loc);
    }
    if (!alone_value.empty()) {
        // A pattern that matches running text cannot delimit a bare option
        // name from the text after it: an option's value runs to the next
        // bar or option name, so ":opt some words" would silently take
        // "some words" as the value and the alone value would never be
        // reached.  A pattern that rejects multi-word text raises a clean
        // argument error there instead.  The test is behavioral rather
        // than a comparison against matches_all()'s one literal pattern,
        // because an @@@argtype written without a :pattern gets ".*",
        // which is equally unable to delimit.
        if (std::regex_match(std::string("one two"), m_types[name].m_regex)) {
            std::stringstream ss {};
            ss << "The argument type \"" << name << "\" cannot declare an :alone value"
               << "because its pattern matches running text:"
               << "  " << pattern << "\n\n"
               << "An :alone value is used when an option name is written without a" 
               << "value.  A type that matches running text cannot tell a bare option"
               << "name from one whose value follows it, so the text after the name"
               << "would be taken as the value instead.";
            throw Definition_error(ss.str(), loc);
        }
        if (!std::regex_match(alone_value, m_types[name].m_regex)) {
            std::stringstream ss {};
            ss << "The alone value \"" << alone_value << "\" for argument type \""
               << name << "\" does not match its own pattern:\n"
               << "  " << pattern << "\n";
            throw Definition_error(ss.str(), loc);
        }
    }
    m_names.push_back(name);
}

void Argtype_registry::add(std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end, std::vector<Katom>& katoms)
{
    (void)K::log(3);
    auto [positional, optional, rest] =
        argument_split(begin + 1, end - 1); //, Argtype_registry::parameters.m_positional.size());

    check_for_existing_definition(positional[0][0].m_text, begin->m_loc);

    auto values = Argtype_registry::parameters().value_map(positional, optional, rest, begin->m_loc);

    // std::for_each(begin, end+1, [](Katom& k) { k.m_type = katom_t::replaced; });

    add(values["name"], values["desc"], values["pattern"], values["default"],
        values["alone"], values["python_cast"], modify_string_f{},
        begin->m_loc);
    
    modify_type(katom_t::replaced, begin, end);
    auto next_iter = end;
    ignore_whitespace(next_iter, katoms);
}

Argtype Argtype_registry::get(const std::string& name, const Locator& loc) const
{
    if (is_not_in(name, m_names)) {
        std::stringstream msg {};
        msg << "Type '" << name << "' is not an argument type";
        throw Argument_error(msg.str(), loc);
    }
    return m_types.at(name);
}


std::string Argtype_registry::eval(
    const std::string& value, const std::string& type_name, const Locator& loc)
{
    std::regex re = m_types[type_name].m_regex;
    std::smatch match {};
    if (std::regex_match(value, match, re)) {
        return value;
    } else {
        //return "NO MATCH";
        std::stringstream ss {};
        ss << "Argument \"" << value << "\" does not match the pattern for \""
           << type_name << "\"\n";
        throw Definition_error(ss.str(), loc);
    }
}

// The values a type supplies when an argument does not give one: the
// default (the option was not written at all) and the alone value (the
// option name was written without a value).  Shown only when declared.
static std::string values_note(const Argtype& type)
{
    std::stringstream note {};
    if (!type.m_default.empty()) {
        note << " [default: " << type.m_default << "]";
    }
    if (!type.m_alone.empty()) {
        note << " [alone: " << type.m_alone << "]";
    }
    return note.str();
}

std::string Argtype_registry::describe(bool long_form, int indent_width) const
{
    std::string indent(' ', indent_width);
    std::size_t name_width = std::accumulate(
        m_names.begin(), m_names.end(), 0,
        [&] (size_t w, const std::string& name) { return std::max(w, name.size()); });
    std::stringstream result {};
    for (const auto& name : m_names) {
        std::string label { "Regex:" };
        int pat_width = name_width + 2 + indent_width + label.size();
        result << indent << std::setw(name_width) << name << sp_arrow;
        if (long_form) {
            result << m_types.at(name).m_desc << "\n";
            result << std::setw(pat_width) << "regex: " << m_types.at(name).m_symbolic_pattern;
            if (m_types.at(name).m_symbolic_pattern != m_types.at(name).m_pattern)
                result << sp_arrow << m_types.at(name).m_pattern;
            result << "\n";
        } else {
            // result << abbrev(m_types.at(name).m_desc) << "\n";
            result << regex_split(m_types.at(name).m_desc, std::regex("\\n"), true)[0]
                   << values_note(m_types.at(name)) << "\n";
        }
    }
    if (long_form) {
        result << "\nA previously defined type can be included in the definition of a new type\n"
               << "by surrounding the name of the existing type in single quotation marks.\n";
    }
    return result.str();
}
