#include "server/GameServer.h"
#include "common/network/Packets.h"
#include "memory/UniquePointer.h"
#include "server/Game.h"
#include "utils/Logger.h"

static Server server;
static HashMap<int, UniquePointer<ServerPlayer>> players;

static void handleControllerPacket(ServerPlayer& p, InPacket& in) {
    PlayerUpdatePacket pup;
    pup.read(in);
    p.onUpdatePacket(pup);
}

struct Receiver {
    void onConnect(Server::Client& client) {
        if(players.tryEmplace(client.getId(), new ServerPlayer(client))) {
            LOG_WARNING(StringBuffer<50>("cannot emplace client id: ")
                            .append(client.getId()));
            server.disconnect(client);
            return;
        }
        auto* p = players.search(client.getId());
        if(p == nullptr) {
            LOG_WARNING("cannot find added client");
            return;
        }
        Game::addPlayer(**p);
    }

    void onDisconnect(Server::Client& client) {
        auto* p = players.search(client.getId());
        if(p == nullptr) {
            LOG_WARNING("disconnecting missing player");
            return;
        }
        Game::removePlayer(**p);
        players.remove(client.getId());
    }

    void onPacket(Server::Client& client, InPacket& in) {
        auto* p = players.search(client.getId());
        if(p == nullptr) {
            LOG_WARNING("packet from missing player");
            return;
        }
        uint16 id;
        if(in.readU16(id)) {
            LOG_WARNING("invalid packet from client without id");
            return;
        }
        switch(id) {
            case Packets::C_CHAT: (*p)->onChat(in); break;
            case Packets::C_PLAYER_UPDATE:
                handleControllerPacket(**p, in);
                break;
            default: LOG_WARNING("invalid packet from client with unknown id");
        }
    }
};

bool GameServer::init(Server::Port port, int maxClients) {
    Error error = server.start(port, maxClients);
    if(error.has()) {
        LOG_ERROR(error.message);
    }
    return error.has();
}

void GameServer::tick() {
    Receiver r;
    server.consumeEvents(r);
}

void GameServer::sendToAll(OutPacket& out) {
    server.send(out);
}