//
// Created by BreizhHardware on 04/12/2024.
//

#include "networking.h"
std::unordered_map<int, std::pair<int, int>> playerPositions;
std::unordered_map<int, std::chrono::time_point<std::chrono::steady_clock>> lastKeepAlive;

bool initServer() {
    std::cout << "Initializing server..." << std::endl;
    if (SDLNet_ResolveHost(&ip, NULL, 1234) == -1) {
        std::cerr << "SDLNet_ResolveHost: " << SDLNet_GetError() << std::endl;
        return false;
    }

    server = SDLNet_TCP_Open(&ip);
    if (!server) {
        std::cerr << "SDLNet_TCP_Open: " << SDLNet_GetError() << std::endl;
        return false;
    }
    std::cout << "Server initialized!" << std::endl;
    return true;
}

void acceptClients() {
    while (game_running) {
        TCPsocket clientSocket = SDLNet_TCP_Accept(server);
        if (clientSocket) {
            clients.push_back(clientSocket);
            int clientId = clients.size() - 1;
            createNewPlayer(clientId);
            updateKeepAlive(clientId);
            Shark shark(0, 0, 0.1, 0.1,0, 150, 150, renderer,players_server);
            shark.updatePosition(0, 0);
            std::thread clientThread([clientSocket, clientId, &shark]() {
                std::thread keepAliveThread(sendKeepAlive, clientSocket);
                keepAliveThread.detach();
                while (running) {
                    std::string message = receiveMessage(clientSocket);
                    if (!message.empty()) {
                        message = processReceivedData(message);
                        std::cout << "Server received: " << message << std::endl;
                        if (message == "keepalive") {
                            updateKeepAlive(clientId);
                        } else if (message.find(";move;") != std::string::npos) {
                            char direction[20];
                            sscanf(message.c_str(), "%d;move;%s", &clientId, &direction);

                            int newX = 0, newY = 0;
                            std::tie(newX, newY) = updatePlayerPosition(clientId, direction);
                            // Mettre à jour la position du joueur
                            players_server[clientId].updatePosition(newX, newY);
                            std::string updatedMessage = std::to_string(clientId) + ";moved;" + std::to_string(newX) + "," + std::to_string(newY);
                            for (TCPsocket client : clients) {
                                sendMessage(client, updatedMessage);
                            }
                        } else if (message.find(";shark_moved;") != std::string::npos) {
                            int sharkId, x, y;
                            sscanf(message.c_str(), "%d;shark_moved;%d,%d", &sharkId, &x, &y);
                            shark.updatePosition(x, y);
                            std::string updatedMessage = std::to_string(sharkId) + ";shark_moved;" + std::to_string(x) + "," + std::to_string(y);
                            for (TCPsocket client : clients) {
                                sendMessage(client, updatedMessage);
                            }
                        }
                    }
                    SDL_Delay(1);
                }
                lastKeepAlive.erase(clientId);
                SDLNet_TCP_Close(clientSocket);
            });
            clientThread.detach();
        }
        SDL_Delay(100);
    }
}

std::vector<std::string> split(const std::string& str, char delimiter) {
    std::vector<std::string> result;
    std::string temp;
    for (char ch : str) {
        if (ch == delimiter) {
            if (!temp.empty()) { // On évite d'ajouter des chaînes vides si nécessaire
                result.push_back(temp);
            }
            temp.clear(); // Réinitialise pour le prochain segment
        } else {
            temp += ch;
        }
    }
    if (!temp.empty()) { // Ajouter le dernier segment s'il existe
        result.push_back(temp);
    }
    return result;
}

std::pair<int, int> updatePlayerPosition(int clientId, const std::string& direction) {
    std::cout << "Updating player position for client " << clientId << " Direction: "<< direction << std::endl;
    auto& pos = playerPositions[clientId];
    int& newX = pos.first;
    int& newY = pos.second;
    int speed = 5;

    std::vector<std::string> directions = split(direction, '-');

    if(directions.size() > 1 ){
        speed = 3;
    }

    for(const std::string dist : directions){
        if (dist == "up") {
            if (newY-speed < 0) {
                newY = 0;
            } else {
                newY -= speed;
            }
        } else if (dist == "down") {
            if (newY+speed > ENV_HEIGHT - windowHeight) {
                newY = ENV_HEIGHT - windowHeight;
            } else {
                newY += speed;
            }
        } else if (dist == "left") {
            if (newX-speed < 0) {
                newX = 0;
            } else {
                newX -= speed;
            }
        } else if (dist == "right") {
            if (newX+speed > ENV_WIDTH - windowWidth) {
                newX = ENV_WIDTH - windowWidth;
            } else {
                newX += speed;
            }
        }
    }

    return {newX, newY};
}

void createNewPlayer(int clientId) {
    if (clientId < 0) {
        std::cerr << "Invalid client ID: " << clientId << std::endl;
        return;
    }
    // Create a new player at a default position (e.g., 0, 0)
    Player newPlayer(0, 0, 5, renderer, clientId);
    players_server.push_back(newPlayer);
    playerPositions[clientId] = {0, 0}; // Initialize player position

    // Send the list of existing players to the new client
    std::string playerListMessage = "playerList;";
    for (auto& player : players_server) {
        auto [x, y] = player.getPlayerPos();
        playerListMessage += std::to_string(player.getPlayerId()) + "," + std::to_string(x) + "," + std::to_string(y) + ";";
    }
    sendMessage(clients[clientId], playerListMessage);

    // Broadcast the new player to all clients
    std::string newPlayerMessage = "newPlayer;" + std::to_string(clientId) + ",0,0;";
    for (TCPsocket client : clients) {
        sendMessage(client, newPlayerMessage);
    }
}

void updateKeepAlive(int clientId) {
    lastKeepAlive[clientId] = std::chrono::steady_clock::now();
}

void checkClientAlive() {
    auto now = std::chrono::steady_clock::now();
    for (auto it = lastKeepAlive.begin(); it != lastKeepAlive.end();) {
        if (std::chrono::duration_cast<std::chrono::seconds>(now - it->second).count() > 5) {
            std::cerr << "Client " << it->first << " is not responding. Removing..." << std::endl;
            SDLNet_TCP_Close(clients[it->first]);
            clients.erase(clients.begin() + it->first);
            playerPositions.erase(it->first); // Supprimer la position du joueur
            it = lastKeepAlive.erase(it);
        } else {
            ++it;
        }
    }
}

void closeServer() {
    std::string quitMessage = "host;quit";
    for (TCPsocket client : clients) {
        sendMessage(client, quitMessage);
    }
    // Fermer les sockets et nettoyer les ressources
    for (TCPsocket client : clients) {
        SDLNet_TCP_Close(client);
    }
    clients.clear();
    SDLNet_TCP_Close(server);
}

void handleServerMessages() {
    std::string message = receiveMessage(server);
    if (!message.empty()) {
        std::cout << "Server received: " << message << std::endl;
        if (message.find(";moved;") != std::string::npos) {
            int clientId, x, y;
            sscanf(message.c_str(), "%d;moved;%d,%d", &clientId, &x, &y);
            // Mettre à jour la position du joueur dans players_server
            for (auto& player : players_server) {
                std::cout << "Player ID: " << player.getPlayerId() << std::endl;
                std::cout << "Client ID: " << clientId << std::endl;
                std::cout << "PlayerId == ClientId: " << (player.getPlayerId() == clientId) << std::endl;
                if (player.getPlayerId() == clientId) {
                    player.updatePosition(x, y);
                    std::cout << "Player " << clientId << " moved to " << player.getUnifiedX() << ", " << player.getUnifiedY() << std::endl;
                    break;
                }
            }
        }
    }
}

void sendSharkPosition(TCPsocket socket, int sharkId, int x, int y) {
    std::string message = std::to_string(sharkId) + ";shark_moved;" + std::to_string(x) + "," + std::to_string(y);
    sendMessage(socket, message);
}

std::string processReceivedData(const std::string& data) {
    std::string filteredData;
    for (char c : data) {
        if (std::isalnum(c) || c == ';' || c == '-' || c == '_' || std::isalpha(c)) {
            filteredData += c;
        }
    }
    return filteredData;
}