#pragma once

#include <limits>
#include <tuple>
#include <vector>
#include "katom.h"
#include "argument.h"
#include "locator.h"

class Argtype_registry;

std::tuple<std::vector<std::vector<Katom>>,std::vector<std::vector<Katom>>,std::vector<Katom>>
argument_split(std::vector<Katom>::const_iterator kbegin, std::vector<Katom>::const_iterator kend,
               long unsigned int positional_limit = std::numeric_limits<int>::max());

// The katoms of a parameter list, split into one list per parameter:
// positional first, then optional (each beginning with its option-name
// katom).  An option set keeps the katoms of its members this way, because
// those katoms are what a declaration using the set ends up declaring.
std::tuple<std::vector<std::vector<Katom>>,std::vector<std::vector<Katom>>>
parameter_split(std::vector<Katom>::const_iterator kbegin, std::vector<Katom>::const_iterator kend);

class Parameter_set
{
public:
    Parameter_set() {};
    ~Parameter_set() = default;
    Parameter_set(const std::string parameter_string);
    Parameter_set(const std::vector<Katom>& katoms);
    Parameter_set(const std::vector<Katom>& katoms, const Argtype_registry& argtypes);

    void parse_parameters(const std::vector<Katom>& katoms, const Argtype_registry& argtypes);
    void describe_parameters();

    void check_positional(
        const std::vector<std::vector<Katom>>& positional_arguments, const Locator& loc);
    std::map<std::string, std::string> check_optional(
        const std::vector<std::vector<Katom>>& optional_arguments, const Locator& loc);
    const std::map<std::string, std::string> value_map(
        const std::vector<std::vector<Katom>>& positional,
        const std::vector<std::vector<Katom>>& optional,
        const std::vector<Katom>& rest,
        const Locator& loc);
    const Parameter* find(const std::string& name) const;
    static void validate(
        const Parameter& parameter, const std::string& value, const Locator& loc);
    bool empty() const { return m_katoms.size() == 0; };

    std::vector<Katom> m_katoms {};
    //Argtype_registry m_argtypes {};
    std::vector<Parameter> m_positional {};
    std::vector<Parameter> m_optional {};
    std::vector<std::string> m_optional_names {};
    std::vector<Parameter> m_rest {};
    size_t m_positional_count = std::numeric_limits<int>::max();

};

bool operator==(Parameter_set lhs, Parameter_set rhs);

// The Argument_set alias is used at application sites, where the set
// describes arguments rather than parameters. See argument.h.
using Argument_set = Parameter_set;

void describe_arguments(
    const std::string& label,
    const std::vector<std::vector<Katom>>& positional,
    const std::vector<std::vector<Katom>>& optional,
    const std::vector<Katom>& rest);

std::string replace_arguments(
    const std::map<std::string, std::string>& values,
    const std::string& parameterized_text, const Locator& loc);
