#ifndef FUNCTION_HPP
#define FUNCTION_HPP

#include "symbol.hpp"
#include <vector>

template<std::size_t N>
using signature_impl = cxstring::string<type_t, N>;

struct signature_t {
    signature_t() = default;
    signature_t(const signature_t&) = delete;
    signature_t(signature_t&&) = default;
    signature_t& operator = (const signature_t&) = delete;
    signature_t& operator = (signature_t&&) = default;

    template<std::size_t N>
    explicit signature_t(const signature_impl<N>& sig) : types(sig.begin(), sig.end()) {}

    std::string serialize() const;
    void push(const symbol& s);
    std::size_t size() const;

    template<typename T>
    bool is(const std::size_t i) const {
        return types[i] == T::static_type_id();
    }

    type_t operator [] (const std::size_t i) const;

    bool operator < (const signature_t& sig) const;

    static const signature_t none;

private :
    std::vector<type_t> types;
};

template<typename ... Args>
struct make_signature_helper;

class function : public symbol_impl_proxy<function> {
    std::string name;
    std::vector<symbol> params;

public :
    explicit function(const std::string& name);
    explicit function(std::string&& name);

    void push(symbol&& s);

    function* clone() const override;
    symbol_impl* evaluate(const interpreter_context& context) const override;
    std::string serialize(const interpreter_context& context) const override;
    static const std::string& static_type_name();
    bool is_litteral() const override;
    std::pair<std::size_t,std::size_t> priority(const interpreter_context&) const override;

    template<typename ... Args>
    static constexpr signature_t make_signature() {
        return signature_t(make_signature_helper<Args...>::make());
    }
};

template<>
struct make_signature_helper<> {
    static constexpr signature_impl<0> make() {
        return signature_impl<0>();
    }
};

template<typename T, typename ... Args>
struct make_signature_helper<T, Args...> {
    static constexpr signature_impl<sizeof...(Args)+1> make() {
        return T::static_type_id() + make_signature_helper<Args...>::make();
    }
};

#endif
