#pragma once

#include <ixwebsocket/IXWebSocketServer.h>

#include <memory>
#include <functional>
#include <thread>
#include <atomic>
#include <nlohmann/json.hpp>

namespace CppModelBase
{
    typedef std::function<nlohmann::json(nlohmann::json &, nlohmann::json &)> ExecutionCallback;

    class SimulationController
    {
    private:
        ExecutionCallback executionCallback;
        std::unique_ptr<ix::WebSocketServer> wsServer;
        std::thread simulationThread;
        std::atomic<bool> running{false};
        int localExecutionCount = 0;

        void Run();
        void StartWebSocketServer(int port, const std::string &host);
        void StopWebSocketServer();
        void InstallSignalHandler();

        void HandleMessage(const std::string &text, ix::WebSocket &ws);
        void HandleRunSimulation(const nlohmann::json &msg, ix::WebSocket &ws);
        void SendError(ix::WebSocket &ws,
                       const std::string &code,
                       const std::string &message,
                       const std::string &requestType);

        void SendResults(ix::WebSocket &ws, const nlohmann::json &results, const std::string &requestType);

    protected:
        unsigned long long stepSize_ms = 1000;
        unsigned long long time_ms = 10000;

        SimulationInputs inputs;
        SimulationOutputs outputs;
        nlohmann::json parameters;

        int simulationResult = true;

        bool runOffline = false;

    public:
        SimulationController();
        ~SimulationController();

        void Start(int wsPort = 9090, const std::string &wsHost = "0.0.0.0");
        void Stop();

        nlohmann::json NextExecution(nlohmann::json &inputs, nlohmann::json &parameters);

        void SetNewExecutionCallback(ExecutionCallback callback);
    };
}