#ifndef FUNCTION_HPP
#define FUNCTION_HPP

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

class function : public symbol_impl {
    std::string name;
    std::deque<symbol> params;

public :
    explicit function(const std::string& n) : name(n) {}

    void push(symbol&& s) {
        params.push_back(std::move(s));
    }

    function* clone() const override {
        function* f = new function(name);
        for (const symbol& p : params)
            f->params.push_back(p.clone());
        return f;
    }

    symbol_impl* evaluate(const interpreter_context& context) const override {
        try {
            return context.call_function(name, params);
        } catch (no_function_match_exception e) {
            context.msg("error: no matching function call to '" + name + "(" + e.prototype + ")'");
            context.msg("note: candidates are:");
            for (const auto& s : e.available)
                context.msg("note: '" + name + "(" + s + ")'");

            throw parser_exception();
        } catch (...) {
            throw;
        }
    }

    std::string serialize(const interpreter_context& context) const override {
        std::string s = name;
        s += "(";
        for (size_t i = 0; i < params.size(); ++i) {
            if (i != 0)
                s += ", ";

            s += params[i].serialize(context);
        }
        s += ")";
        return s;
    }

    const std::string& type() const override {
        return type_name;
    }

    bool is_litteral() const override {
        return false;
    }

    static const std::string type_name;
};

#endif
