#include <cassert>

#include "network/Client.h"

Client::Client() : client(nullptr), connection(nullptr) {
    ENet::add();
}

Client::~Client() {
    disconnect();
    enet_host_destroy(client);
    ENet::remove();
}

Error Client::start() {
    assert(client == nullptr);
    if(ENet::hasError()) {
        return {"cannot initialize enet"};
    }
    client = enet_host_create(nullptr, 1, 2, 0, 0);
    if(client == nullptr) {
        return {"cannot create enet client host"};
    }
    return {};
}

Error Client::connect(const char* server, Port port, int timeout) {
    assert(client != nullptr);
    assert(connection == nullptr);
    ENetAddress address;
    memset(&address, 0, sizeof(ENetAddress));
    ENetEvent event;
    enet_address_set_host(&address, server);
    address.port = port;

    connection = enet_host_connect(client, &address, 3, 0);
    if(connection == nullptr) {
        return {"server is not available"};
    }

    if(enet_host_service(client, &event, timeout) <= 0 ||
       event.type != ENET_EVENT_TYPE_CONNECT) {
        disconnect();
        return {"connection failed"};
    }
    return {};
}

void Client::disconnect() {
    if(connection == nullptr) {
        return;
    }
    ENetEvent e;
    enet_peer_disconnect(connection, 0);
    while(enet_host_service(client, &e, 3000) > 0) {
        switch(e.type) {
            case ENET_EVENT_TYPE_RECEIVE: enet_packet_destroy(e.packet); break;
            case ENET_EVENT_TYPE_DISCONNECT: connection = nullptr; return;
            default: break;
        }
    }
    enet_peer_reset(connection);
    connection = nullptr;
}

void Client::send(OutPacket& p) {
    assert(client != nullptr);
    if(p.packet != nullptr) {
        p.packet->dataLength = p.index;
        enet_peer_send(connection, p.channel, p.packet);
        p.packet = nullptr;
    }
}