#include "function.hpp"
#include "context.hpp"

const signature_t signature_t::none;

std::string signature_t::serialize() const {
    std::string str;
    const std::size_t n = size();
    for (std::size_t i = 0; i < n; ++i) {
        if (i != 0) str += ", ";
        str += type_helper::get_type_name(types[i]);
    }

    return str;
}

void signature_t::push(const symbol& s) {
    types.push_back(s.type_id());
}

std::size_t signature_t::size() const {
    return types.size();
}

type_t signature_t::operator[] (const std::size_t i) const {
    return types[i];
}

bool signature_t::operator< (const signature_t& sig) const {
    if (sig.size() != size()) return size() < sig.size();

    const std::size_t n = size();
    for (std::size_t i = 0; i < n; ++i) {
        if (types[i] != sig.types[i]) return types[i] < sig.types[i];
    }

    return false;
}

function::function(const std::string& name) : name(name) {}
function::function(std::string&& name) : name(name) {}

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

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

symbol_impl* function::evaluate(const interpreter_context& context) const {
    try {
        return context.call_function(name, params);
    } catch (no_function_match_exception& e) {
        context.error(e);
        throw parser_exception();
    } catch (...) {
        throw;
    }
}

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

const std::string& function::static_type_name() {
    static const std::string name = "function";
    return name;
}

bool function::is_litteral() const {
    return false;
}

std::pair<std::size_t,std::size_t> function::priority(const interpreter_context&) const {
    return std::pair<std::size_t,std::size_t>(0, 0);
}
