#include "rpc_server.hpp"
#include "vm_provider.hpp"
#include <memory>

namespace sliger::rpc::action {

struct Action {
    virtual auto perform_action(
        std::unique_ptr<sliger::rpc::BufferedWriter> writer,
        sliger::rpc::vm_provider::VmProvider vm_provider) -> void = 0;
    virtual ~Action() = default;
};

class FlameGraph : public Action {
public:
    FlameGraph() { }
    auto perform_action(std::unique_ptr<sliger::rpc::BufferedWriter> writer,
        sliger::rpc::vm_provider::VmProvider vm_provider) -> void;
};

class CodeCoverage : public Action {
public:
    CodeCoverage() { }
    auto perform_action(std::unique_ptr<sliger::rpc::BufferedWriter> writer,
        sliger::rpc::vm_provider::VmProvider vm_provider) -> void;
};

class RunDebug : public Action {
public:
    RunDebug(std::vector<uint32_t> instructions)
        : instructions(instructions)
    {
    }
    auto perform_action(std::unique_ptr<sliger::rpc::BufferedWriter> writer,
        sliger::rpc::vm_provider::VmProvider vm_provider) -> void;

private:
    std::vector<uint32_t> instructions;
};

static auto action_from_json(
    std::unique_ptr<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 == "flamegraph") {
        auto action = FlameGraph();
        return std::make_unique<FlameGraph>(action);
    }

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

    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) {
            std::unique_ptr<json::Value> moved = std::move(v);
            auto value = moved->as<sliger::json::Number>().value;
            instructions.push_back((uint32_t)value);
        }
        auto action = RunDebug(instructions);
        return std::make_unique<RunDebug>(action);
    }
    throw "todo";
};
}