#include "ModuleHTTP.h"
#include <httplib.h>
#include <iostream>
#include <thread>

//#include <wait.h>

#include <chrono>

using namespace httplib;
namespace mdd{

    bool ModuleHTTP::connect(){
        Client cli(_id, _port);
        std::string body;
        auto res = cli.Get("/status",
                           [&](const char *data, size_t data_length) {
                               body.append(data, data_length);
                               return true;
                           });
        if(body.empty()){
            return false;
        }
        body =  std::string(R"()") + body;
        json status = json::parse(body.c_str());
        if (status["status"].get<std::string>() == "ready"){
            return true;
        }
        else{
            return false;
        }
    }

    std::string ModuleHTTP::str_to_json(const std::string& input){
        if(input.empty()){
            return input;
        }
        std::string str = input;
        size_t start_pos = 0;
        while((start_pos = str.find('[', start_pos)) != std::string::npos) {
            str.replace(start_pos, 1, "{");
            start_pos += 1;
        }
        start_pos = 0;
        while((start_pos = str.find(']', start_pos)) != std::string::npos) {
            str.replace(start_pos, 1, "}");
            start_pos += 1;
        }
        return str;
    }

    bool ModuleHTTP::updateInputs() {
        Client cli(_id, _port);

        std::string body;
        auto res = cli.Get("/inputs",
                           [&](const char *data, size_t data_length) {
                               body.append(data, data_length);
                               return true;
                           });
        assert(res->body.empty());
        body =  std::string(R"()") + body;
        json inputs = json::parse(body.c_str());

        for(int j = 0; j < (int)getInputs().size()-(int)inputs.size(); ++j){
            pop_backInput();
        }
        for(size_t i=0; i < getInputs().size(); i++){
            getInput(i)->setType(inputs[i]["type"].get<std::string>());
            getInput(i)->setAppendix(i);
            getInput(i)->setDefaultValue() = inputs[i];
        }
        for(int index = getInputs().size(); index < inputs.size(); ++index){
            json val;
            val["value"] = inputs[index]["value"];
            addInput(inputs[index]["type"].get<std::string>(),val);
        }
        return true;
    }

    bool ModuleHTTP::updateOutputs() {
        Client cli(_id, _port);

        std::string body;
        auto res = cli.Get("/outputs",
                           [&](const char *data, size_t data_length) {
                               body.append(data, data_length);
                               return true;
                           });

        assert(res->body.empty());
        body =  std::string(R"()") + body;
        json outputs = json::parse(body);

        for(int j = 0; j < (int)getOutputs().size()-(int)outputs.size(); ++j){
            pop_backOutput();
        }
        for(size_t i=0; i < getOutputs().size(); i++){
            getOutput(i)->setType(outputs[i]["type"].get<std::string>());
            getOutput(i)->setAppendix(i);
            getOutput(i)->getValueInternal() = outputs[i];
        }
        for(int index = getOutputs().size(); index < outputs.size(); ++index){
            json val;
            val["value"] = outputs[index]["value"];
            addOutput(outputs[index]["type"].get<std::string>(),val);
        }
        return true;
    }

    void ModuleHTTP::updateLayout(){
        updateInputs();
        updateOutputs();
    }

    ModuleHTTP::ModuleHTTP(std::string fname, std::string id, int port):
        _fname(fname),
        _id(id),
        _port(port)
    {
        setType("HTTP");

        if(!fname.empty()){
            _child = child("python3 " + fname + " " + std::to_string(port));
        }
        while(!connect()){
            std::this_thread::sleep_for(std::chrono::microseconds(500));
        }
        updateLayout();
    }

    ModuleHTTP::~ModuleHTTP()
    {
        _child.terminate();
        _child.join();
        
    }

    bool ModuleHTTP::update() {
        Client cli(_id, _port);
        json new_inputs;
        for (int i = 0; i < getInputs().size(); ++i) {
            new_inputs.push_back(getInput(i)->getValue());
        }
        std::string content = new_inputs.dump();
        auto res = cli.Post("/update",content.size(),
                            [&](size_t offset, size_t length, DataSink &sink) {
                                sink.write(content.data() + offset, length);
                                return true; // return 'false' if you want to cancel the request.
                            },"application/json");
        while(!connect()){
            std::this_thread::sleep_for(std::chrono::microseconds(500));
        }
        return updateOutputs();
    }
}