#pragma once

#include <string>
#include <vector>
#include <map>

#include "argtype.h"
#include "katom.h"
#include "argument_set.h"

class Argtype_registry
{
public:
    static Parameter_set& parameters();

    Argtype_registry();

    std::string replace_symbols(const std::string& pattern, const Locator& loc);
    void check_for_existing_definition(const std::string& name, const Locator& loc);
    void 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 add(std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end, std::vector<Katom>& katoms);

    Argtype get(const std::string& name, const Locator& loc) const;

    std::string eval(const std::string& value, const std::string& type_name, const Locator& loc);
    std::string describe(bool long_form=false, int indent_width=4) const;

    inline bool is_defined(const std::string& name) const {
        return count(m_names.begin(), m_names.end(), name) > 0;
    }
    std::vector<std::string> m_names {};
    std::map<std::string, Argtype> m_types {};
    size_t m_name_size = 0;
    size_t m_pattern_size = 0;
};

const
std::string default_argtype = "string";

const
std::vector<std::tuple<std::string, std::string, std::string, std::string, modify_string_f>> base_argtypes = {
    { "string", "arbitrary text",
      R"((?:.|\n)*)", "str",
      pyformat_string },

    { "word", "a series of characters without a space",
      R"([^\s]+)", "str",
      pyformat_string },

    { "bool", "a Boolean value of 'false', 'False', '0', 'true', 'True', or '1'", 
      R"(0|1|true|false|True|False)",
      "(lambda b : True if b not in {'0','false','False'} else False)",
      pyformat_bool }, 

    { "uint", "an integer greater than or equal to zero",
      R"(\d\d*)", "int",
      pyformat_number },

    { "int","an integer",
      R"([-+]?'uint')", "int",
      pyformat_number },

    { "float", "a floating-point number",
      R"([-+]?(\d+(\.\d*)?|\.\d+))", "float",
      pyformat_number },

    { "fraction", "a number in the form 'n/d'",
      R"('int'/'uint')", "(lambda f : float(f.split('/')[0]) / float(f.split('/')[1]))",
      pyformat_number },

    { "list", "list of elements separated by whitespace",
      R"((?:.|\n)*)", "(lambda s : s.split())",
      pyformat_list },

    { "dlist", "list of elements separated by the first word",
      R"((?:.|\n)*)", "",
      pyformat_dlist },

    { "rest", "the remaining arguments as a list nested to N dimensions "
      "(rest(N)); the delimiter for dimension n is a run of n bar "
      "characters, so | separates elements and || lists of elements",
      R"((?:.|\n)*)", R"((lambda s : __import__("kutil").rest_split(s, N)))",
      nullptr },

    { "literal", "literal text passed without interpretation",
      R"((?:.|\n)*)", "str",
      pyformat_string },
};
