#include <sstream>
#include <memory>
#include "pilang.hpp"
using namespace Pilang3;


class Std {
public:
    static Variable exit(SharedEnvironment, Cmdargs& args) {
        throw exceptions::exit(std::get<integer_type>(args[0].data));
    }

    static Variable retval(SharedEnvironment, Cmdargs& args) {
        return Variable({
            Variable::id_retval,
            std::make_shared<Variable>(args[0])
        });
    }

    static Variable set(SharedEnvironment env, Cmdargs& args) {
        auto varName = std::get<std::string>(args[0].data);

        auto& scope = env->currScope();
        auto res = scope.find(varName);
        if (res != scope.end()) {
            *res->second = args[1];
        } else {
            scope[varName] = std::make_shared<Variable>(args[1]);
        }
        return args[1];
    }
    static Variable local(SharedEnvironment env, Cmdargs& args) {
        env->currScope()[std::get<std::string>(args[0].data)] = std::make_shared<Variable>(args[1]);
        return args[1];
    }
    static Variable global(SharedEnvironment env, Cmdargs& args) {
        (*env->globalScope)[std::get<std::string>(args[0].data)] = std::make_shared<Variable>(args[1]);
        return args[1];
    }

    static Variable concat(SharedEnvironment, Cmdargs& args) {
        std::ostringstream fres;
        for (const auto& arg : args) {
            switch (arg.type) {
                case Variable::id_integer: fres << std::get<integer_type>(arg.data); break;
                case Variable::id_string: fres << std::get<std::string>(arg.data); break;
                case Variable::id_reference: fres << "REF<" << std::get<std::string>(arg.data) << '>'; break;
                case Variable::id_type: fres << std::get<Variable::Type>(arg.data); break;
                default: fres << "<ID" << arg.type << '>'; break;
            }
        }
        return Variable({
            Variable::id_string,
            fres.str()
        });
    }

    Std() {
        builtinCmds["exit"] = {exit, {Variable::id_integer}, false};
        builtinCmds["return"] = {retval, {Variable::id_any}, false};
        builtinCmds["="] = builtinCmds["set"] = {set, {Variable::id_string, Variable::id_any}, false};
        builtinCmds[":="] = builtinCmds["local"] = {local, {Variable::id_string, Variable::id_any}, false};
        builtinCmds["!="] = builtinCmds["global"] = {global, {Variable::id_string, Variable::id_any}, false};
        builtinCmds["concat"] = {concat, {}, true};
    }
};

static Std inst;