#include "actions.hpp"
#include "json.hpp"
#include "vm_provider.hpp"
#include <cstdlib>
#include <iostream>
#include <memory>

using namespace sliger::rpc::action;

auto Status::perform_action(
    std::unique_ptr<sliger::rpc::BufferedWriter> writer, VmProvider& vm) -> void
{
    bool running = not vm.done();

    writer->write(std::format(
        "{{ \"ok\": true, \"status\": {{ \"running\": {} }} }}", running));
    writer->flush();
};

auto FlameGraph::perform_action(
    std::unique_ptr<sliger::rpc::BufferedWriter> writer, VmProvider& vm) -> void
{
    auto json = vm.flame_graph_json();
    if (json) {
        writer->write(std::format(
            "{{ \"ok\": true, \"flameGraph\": {}  }}", json.value()));
    } else {
        writer->write("{ \"ok\": false }");
    }
    writer->flush();
};

auto CodeCoverage::perform_action(
    std::unique_ptr<sliger::rpc::BufferedWriter> writer, VmProvider& vm) -> void
{
    auto json = vm.code_coverage_json();
    if (json) {
        writer->write(std::format(
            "{{ \"ok\": true, \"codeCoverage\": {} }}", json.value()));
    } else {
        writer->write("{ \"ok\": false }");
    }
    writer->flush();
};

auto RunDebug::perform_action(
    std::unique_ptr<sliger::rpc::BufferedWriter> writer, VmProvider& vm) -> void
{
    auto program = this->instructions;
    vm.load_and_start(program);
    writer->write("{ \"ok\": true }");
    writer->flush();
};

auto sliger::rpc::action::action_from_json(
    const sliger::json::Value& value) -> std::unique_ptr<Action>
{
    auto& obj = value.as<sliger::json::Object>();
    auto type = obj.fields.at("type")->as<sliger::json::String>();

    if (type.value == "status") {
        return std::make_unique<Status>();
    }

    if (type.value == "flame-graph") {
        return std::make_unique<FlameGraph>();
    }

    if (type.value == "code-coverage") {
        return std::make_unique<CodeCoverage>();
    }

    if (type.value == "run-debug") {
        sliger::json::ArrayValues values = std::move(
            obj.fields.at("program")->as<sliger::json::Array>().values);
        auto instructions = std::vector<uint32_t>();
        for (auto&& v : values) {
            auto value = v->as<sliger::json::Number>().value;
            instructions.push_back((uint32_t)value);
        }
        return std::make_unique<RunDebug>(instructions);
    }
    std::cout << "error: TODO " << __FILE__ << ":" << __LINE__ << "\n";
    exit(1);
};
