#include <string>
#if (defined (WIN32))
    #include <Windows.h>
#endif
#include <json.hpp>
#include <zmq.hpp>
#include "zhelpers.hpp"

#include "ProcessorStandard.h"
#include "Connector.h"

using namespace mdd;
class Server {
private:
    zmq::context_t context;
    Connector _connector = Connector();

    json msg_header() {
        json jmsg;
        jmsg["serverID"] = id;
        jmsg["msgNr"] = counter;
        return jmsg;
    }

    void send_alive() {
        std::string msg = msg_header().dump();

        //  Send reply back to client
        zmq::message_t reply(msg.size());
        memcpy(reply.data(), msg.c_str(), msg.size());
        reply_socket.send(reply);
        std::cout << "[alive]: " << msg << std::endl;
    }

    void get_request(json request) {

        json jmsg = msg_header();
        if (request.is_array())
        {
            for (size_t i = 0; i < request.size(); i++)
            {
                jmsg["receive"].push_back(_connector.decode(request[i]));
            }
        }
        else {
            //json test;
            //test = _connector.decode(request);
            //std::cout << "[Server]: get_request: " << test.dump() << std::endl;
            jmsg["receive"] = _connector.decode(request);
        }

        std::string msg = jmsg.dump();

        //  Send reply back to client
        zmq::message_t reply(msg.size());
        memcpy(reply.data(), msg.c_str(), msg.size());
        reply_socket.send(reply);

        std::cout << "[receive]: " << msg << std::endl;
    }

    void try_request(json request) {
        //auto start = std::chrono::steady_clock::now();
        json jmsg = msg_header();
        if (request.is_array())
        {
            for (size_t i = 0; i < request.size(); i++)
            {
                jmsg["change"].push_back(_connector.decode(request[i]));
            }
        }
        else {
            jmsg["change"] = _connector.decode(request);
        }

        sendChange(jmsg);
        
        //auto end = std::chrono::steady_clock::now();
        //std::chrono::duration<double> elapsed_seconds = middle - start;
        //std::chrono::duration<double> elapsed_seconds2 = end - middle;
        //std::cout << "[Server]: 1:" << elapsed_seconds.count() << " s 2:" << elapsed_seconds2.count() << "s\n";
    }

    json sendChange(const json& jchange) {
        json jmsg = jchange;
        if (!jmsg.contains("change"))
        {
            jmsg = msg_header();
            jmsg["change"] = jchange;
        }
        
        std::string smsg = jmsg.dump();
        std::string stopic = "CHANGE";
        zmq::message_t topic(stopic.size());
        zmq::message_t msg(smsg.size());
        memcpy(topic.data(), stopic.c_str(), stopic.size());
        memcpy(msg.data(), smsg.c_str(), smsg.size());
        //auto middle = std::chrono::steady_clock::now();
        try {
            std::cout << "[change]: " << stopic << ": " << smsg << std::endl;
            s_sendmore(publisher_socket, stopic);
            s_send(publisher_socket, smsg);
            //publisher_socket.send(topic, ZMQ_SNDMORE);
            //publisher_socket.send(msg);
        }
        catch (zmq::error_t& e) {
            std::cout << e.what() << std::endl;
        }
        ++counter;
        return jmsg;
    }

public:
    zmq::socket_t reply_socket;
    zmq::socket_t publisher_socket;
    
    uint32_t id = 0;
    size_t counter = 0;

    Server()
        : context(1)
        , reply_socket(context, zmq::socket_type::rep)
        , publisher_socket(context, zmq::socket_type::pub)
    {
        id = reinterpret_cast<uint32_t>(this);
        reply_socket.bind("tcp://*:5555");
        publisher_socket.bind("tcp://*:5556");
        _connector.attachCallback(std::bind(&Server::sendChange,this,std::placeholders::_1));
    }

    void handle_request() {
        zmq::message_t request;
        //  Wait for next request from client
        try
        {
            reply_socket.recv(&request);
            std::cout << request.to_string() << std::endl;
        }
        catch (const std::exception&)
        {
            std::cout << "[Server] received wrong msg!" << std::endl;
        }
        
        json msg;
        try
        {
            msg = json::parse(request.to_string());
        }
        catch (const std::exception&)
        {
            std::cout << "[Server] received wrong msg: " << request.to_string() << std::endl;
        }

        json ans = msg_header();

        //check for missmatch
        if (msg.contains("serverID") && msg.contains("msgNr"))
        {
            if (msg["serverID"] != ans["serverID"] || msg["msgNr"] != ans["msgNr"])
            {
                return send_alive();
            }
        }
        else {
            return send_alive();
        }


        if (!msg.contains("context"))
        {
            return send_alive();
        }
        json jcontext = msg["context"];

        if (jcontext.is_array())
        {
            for (size_t i = 0; i < jcontext.size(); i++)
            {
               
                if (jcontext[i].contains("try"))
                {
                    try_request(jcontext[i]["try"]);
                    send_alive();
                }
                if (jcontext[i].contains("get"))
                {
                    get_request(jcontext[i]["get"]);
                }
            }
        }
        else {
            if (jcontext.contains("try"))
            {
                try_request(jcontext["try"]);
                send_alive();
            }
            if (jcontext.contains("get"))
            {
                get_request(jcontext["get"]);
            }
        }
    }
};


int main(int argc, char* argv[]) {
    Server server;

    while (true) {
        server.handle_request();
        std::cout << "Handeld" << std::endl;
    }
    return 0;
}