#include <iostream>

#include "common/enet/enet.h"

#include "server/network/Server.h"
#include "common/network/Packet.h"

Server::Server(u16 port) : server(nullptr) {
    if(enet_initialize() != 0) {
        std::cout << "an error occurred while initializing ENet\n";
        return;
    }

    ENetAddress address;
    address.host = ENET_HOST_ANY; // localhost
    static_assert(sizeof (enet_uint16) == sizeof (u16), "enet_uint16 and u16 are not of equal size");
    address.port = port;

    server = enet_host_create(&address, clients.getLength(), 2, 0, 0);
    if(server == nullptr) {
        std::cout << "an error occurred while trying to create an ENet server host\n";
    }
}

Server::~Server() {
    enet_host_destroy(server); // safe to call for nullptr
    enet_deinitialize();
}

bool Server::hasError() const {
    return server == nullptr;
}

void Server::consumeEvents(GameServer& gameServer) {
    ENetEvent event;
    while(enet_host_service(server, &event, 0) > 0) {
        switch(event.type) {
            case ENET_EVENT_TYPE_CONNECT:
                onConnect(event, gameServer);
                break;
            case ENET_EVENT_TYPE_RECEIVE:
                onPackage(event, gameServer);
                break;
            case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
            case ENET_EVENT_TYPE_DISCONNECT:
                onDisconnect(event, gameServer);
                break;
            case ENET_EVENT_TYPE_NONE:
                return;
        }
    }
}

void Server::onConnect(ENetEvent& event, GameServer& gameServer) {
    event.peer->data = nullptr;
    uint index = getFreeIndex();
    std::cout << "INDEX: " << index << "\n";
    if(index >= clients.getLength()) {
        Client c;
        c.connect(event.peer);
        gameServer.onFullServerClientConnect(c);
        return;
    }
    clients[index].connect(event.peer);
    event.peer->data = (clients + index);
    gameServer.onClientConnect(clients[index]);
}

void Server::onPackage(ENetEvent& event, GameServer& gameServer) {
    if(event.peer->data != nullptr) {
        Client* c = static_cast<Client*> (event.peer->data);
        Packet p(event.packet);
        gameServer.onClientPackage(*c, p);
    } else {
        std::cout << "receiving package from client without a client structure\n";
    }
}

void Server::onDisconnect(ENetEvent& event, GameServer& gameServer) {
    if(event.peer->data != nullptr) {
        Client* c = static_cast<Client*> (event.peer->data);
        gameServer.onClientDisconnect(*c);
        c->disconnect();
    } else {
        std::cout << "disconnecting a client without a client structure\n";
    }
}

uint Server::getFreeIndex() const {
    for(uint i = 0; i < clients.getLength(); i++) {
        if(clients[i].isDisconnected()) {
            return i;
        }
    }
    return clients.getLength();
}