#include "Server.h"

void Server::start(){
    std::cout << "Initialising TCP sever" << std::endl;
    server.setup(port);
    osc_sender.setup(OSC_HOST, OSC_PORT);
    is_active = true;
    previous_embedding = embedding;
    last_change_time = std::chrono::steady_clock::now();
    lastUpdateTime = std::chrono::steady_clock::now();

    /* allocate pixels in vector */
    esp_pixels.resize(4);
    for(auto& p : esp_pixels){
        p.allocate(128, 168, GL_RGB);
    }

    /* set initial chosen node */
    chosen_node = &nodes[0];
}

void Server::update(ofFbo& esp_comp){
    /* set all past clients to false, to check connection */
    for(auto& c : clients){
        c.second.connected = false;
    }

    ofPixels fboPixels;
    esp_comp.readToPixels(fboPixels);

    vector<ofImage> extractedImages;
    extractedImages.resize(4);

    for (int i = 0; i < 4; i++) {
        ofPixels cropPixels;
        fboPixels.cropTo(cropPixels, regions[i][0], regions[i][1], regions[i][2], regions[i][3]);
        extractedImages[i].setFromPixels(cropPixels);
    }


    for ( int i = 0; i < server.getLastID(); i++){
        if (server.isClientConnected(i)) {
            const int buffer_size = 8;
            char buffer[buffer_size];
            int bytes_recieved = server.receiveRawBytes(i, buffer, buffer_size);
            
            if (bytes_recieved == buffer_size){
                float value;
                int id;
                memcpy(&value, buffer, sizeof(float));
                memcpy(&id, buffer + sizeof(float), sizeof(int));

                std::string ip_address = server.getClientIP(i);

                addOrUpdateClient(id, value, ip_address, true);

                //std::cout << ip_address << " : " << id <<  std::endl;
            }

            
        }
    }

    sendImagesToClients(extractedImages);

    updateEmbedding();
    
    /* check if inactive -> query vptree -> move camera */
    checkActivity();
}

void Server::disconnectAllClients(){
    server.disconnectAllClients();
}

void Server::addOrUpdateClient(int client_id, float value, const std::string& ip_address, bool _connected){
    ClientInfo client;

    client.ip_address = ip_address;
    client.value = value;
    client.connected = _connected;

    clients[client_id] = client;
}


void Server::updateEmbedding() {
    std::vector<std::string> emotionNames = {
        "happy", "sad", "angry", "neutral"
    };

    for (const auto& c : clients) {
        const ClientInfo& info = c.second;
        float val = std::round(info.value * 100.0f) / 100.0f;
        
        if (c.first >= 0 && c.first < emotionNames.size()) {
            embedding.emotions[emotionNames[c.first]] = val;
        }

    }
}

void Server::printClients(){
    for( const auto& c : clients){
        int id = c.first;
        const ClientInfo& info = c.second;
        std::cout << "id: " << id
            << ", value: " << info.value
            << ", IP: " << info.ip_address << std::endl;
    }

    std::cout << is_active << std::endl;
}

void Server::print(){
    const std::vector<std::string> emotionNames = {
        "happy", "sad", "angry", "neutral"
    };

    std::ostringstream ss;

    ss << "Embedding [";
    
    for (size_t i = 0; i < emotionNames.size(); ++i) {
        if (i > 0) ss << ", ";
        ss << std::fixed << std::setprecision(2) << embedding.emotions[emotionNames[i]];
    }

    ss << "]" << endl;
    
    ofSetColor(255, 255, 255);
    ofDrawBitmapString(ss.str(), 20 , 20);
}

/* check if the controllers are in use */
void Server::checkActivity(){
    if (previous_embedding.emotions != embedding.emotions) {  // Check if embedding has changed
        last_change_time = std::chrono::steady_clock::now();  // Reset the timer if there is a change
        previous_embedding = embedding;  // Update the previous embedding to the current one
        is_active = true;

        /* send a query to search the closest embedding in the vp-tree */
        vector<vector<float>> vp_queries;
        vp_queries.push_back(embedding.to_vector());
        vector<int> ids = tree.query(vp_queries, 1);
        chosen_node = &nodes[ids[0]];
        // ofLog() << chosen_node->collider->getPosition();

    } else {
        // Calculate the time since the last change
        auto now = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - last_change_time).count();

        if (duration >= 2) {
            is_active = false;
            //ofLogNotice() << "Inactive";
        }
    }
}

/* send osc msg, check if audio file exists and it is different to the past audiofile */
void Server::sendOSCMessage(){
    std::vector<ofxOscMessage> messages;

    ofxOscMessage me_0;
    ofxOscMessage me_1;
    ofxOscMessage me_2;
    ofxOscMessage me_3;
    ofxOscMessage me_file;
    ofxOscMessage bang_new_mesh;

    bang_new_mesh.setAddress("/emote/bang");
    bang_new_mesh.addTriggerArg();
    messages.push_back(bang_new_mesh);

    for (auto& msg : messages){
         osc_sender.sendMessage(msg, false);
    }
   
}

/* sends request to http server if is_active = true */
void Server::sendHttpRequest(){
    vp_resp = http.query(embedding);
}

std::vector<std::vector<double>> Server::generateRandomVectors(int count, int dimension){
    return tree.generateRandomVectors(count, dimension);
}

void Server::sendImagesToClients(vector<ofImage>& imgs){
    /* convert to pixels */
    int c = 0;
    for(const auto& f :imgs){
        esp_pixels[c] = f.getPixels();
        c++;
    }

    int num_images = imgs.size();
    if (num_images != 4) {
        // No images to send, return early
        return;
    }

    ofSetLogLevel(OF_LOG_FATAL_ERROR);

    for (int i = 0; i < server.getLastID(); i++) {
        if (server.isClientConnected(i)) {     
                
            // Send RGB pixels as raw bytes & check if the ip addresses align -> get id
            for(const auto& c_pair : clients){
                //std::cout << c_pair.first << std::endl;  
                if(c_pair.second.ip_address == server.getClientIP(i)){
                    
                    unsigned char* image_data = esp_pixels[c_pair.first].getData();
                    server.sendRawBytes(i, (char*)image_data, esp_pixels[c_pair.first].size());
                }
            }            
        }
    }
    ofSetLogLevel(OF_LOG_NOTICE);
}

Node& Server::getChosenNode(){
    return *chosen_node;
}

void Server::close(){
    server.close();
}