/******************************************************************************!
 * \file main.cpp
 * \author Sebastien Beaugrand
 * \sa http://beaugrand.chez.com/
 * \copyright CeCILL 2.1 Free Software license
 ******************************************************************************/
#include <thread>
#include <variant>
#include <iostream>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include "build/abstractstubserver.h"

/******************************************************************************!
 * \class Machine
 ******************************************************************************/
namespace state {
struct Ready {};
struct NotReady {};
struct Exit {};
}
using State = std::variant<state::Ready,
                           state::NotReady,
                           state::Exit>;

namespace event {
struct Init {};
struct Exit {};
}
using Event = std::variant<event::Init,
                           event::Exit>;

State
onEvent(state::Ready state, const event::Init&) {
    return state;
}

State
onEvent(state::Ready  /*state*/, const event::Exit&) {
    return state::Exit{};
}

State
onEvent(state::NotReady  /*state*/, const event::Init&) {
    return state::Ready{};
}

State
onEvent(state::NotReady  /*state*/, const event::Exit&) {
    return state::Exit{};
}

State
onEvent(state::Exit state, const event::Init&) {
    return state;
}

State
onEvent(state::Exit state, const event::Exit&) {
    return state;
}

class Machine
{
public:
    ~Machine() {
        std::cout << "Machine::dtor" << std::endl;
    }
    void processEvent(const Event& event) {
        state = std::visit(
            [](const auto& st, const auto& ev) { return onEvent(st, ev); },
            state, event);
    }
    State state = state::NotReady{};
};

/******************************************************************************!
 * \class Server
 ******************************************************************************/
class Server : public AbstractStubServer
{
public:
    Server(jsonrpc::AbstractServerConnector& conn, Machine& m)
        : AbstractStubServer(conn, jsonrpc::JSONRPC_SERVER_V1V2)
        , machine(m) {
        this->StartListening();
    }
    virtual ~Server() {
        std::cout << "Server::dtor" << std::endl;
        this->StopListening();
    }
    virtual std::string status() override {
        return "ok";
    }
    virtual Json::Value object() override {
        Json::Value result;
        result["value"] = 0;
        if (std::holds_alternative<state::Ready>(machine.state)) {
            result["state"] = "ready";
        } else {
            result["state"] = "not ready";
        }
        return result;
    }
    virtual void quit() override {
        std::cout << "Server::quit" << std::endl;
        machine.processEvent(event::Exit{});
    }
private:
    Machine& machine;
};

/******************************************************************************!
 * \fn main
 ******************************************************************************/
int
main(int, char**)
{
    Machine machine;
    jsonrpc::HttpServer httpserver(8383, "", "", 2);  // 2 threads
    Server server(httpserver, machine);

    using namespace std::chrono_literals;
    std::this_thread::sleep_for(2s);
    machine.processEvent(event::Init{});
    for (;;) {
        std::this_thread::sleep_for(100ms);
        if (std::holds_alternative<state::Exit>(machine.state)) {
            break;
        }
    }

    return 0;
}