#include "Server.h"
#include "Protocoll.h"

#include <string>
#include <iostream>
#include <thread>

Server::Server(int port) : m_maxClients(constants::MAX_CLIENTS), m_clientConnected{0}
{
    udpSocket = SDLNet_UDP_Open(port);

    if (udpSocket == nullptr)
    {
        std::cout << "\tSDLNet_UDP_Open failed : " << SDLNet_GetError() << std::endl;
    }
    std::cout << "Server was started and is listening on port: " << port << std::endl;

    //Creating ConnectionAccepted packet!
    // Allocate udp packet with 512 byte size
    connectionAccepted = SDLNet_AllocPacket(512);

    if (connectionAccepted == nullptr)
    {
        std::cout << "\tSDLNet_AllocPacket failed : " << SDLNet_GetError() << std::endl;
    }
    ConnectionAccepted conAcc = ConnectionAccepted();
    memcpy(connectionAccepted->data, &conAcc, sizeof(ConnectionAccepted));
    connectionAccepted->len = sizeof(ConnectionAccepted);

    connectionDenied = SDLNet_AllocPacket(512);

    if (connectionDenied == nullptr)
    {
        std::cout << "\tSDLNet_AllocPacket failed : " << SDLNet_GetError() << std::endl;
    }
    ConnectionDenied conDen = ConnectionDenied();
    memcpy(connectionDenied->data, &conDen, sizeof(ConnectionDenied));
    connectionDenied->len = sizeof(ConnectionDenied);

    //Creating receiver packet!
    receivedPacket = SDLNet_AllocPacket(512);

    if (receivedPacket == nullptr)
    {
        std::cout << "\tSDLNet_AllocPacket failed : " << SDLNet_GetError() << std::endl;
    }
}

int Server::FindFreeClientIndex() const
{   
    for (int i = 0; i < m_maxClients; ++i)
    {
        if (!m_clientConnected[i])
            return i;
    }
    return -1;
}

int Server::FindExistingClientIndex(const IPaddress &ip) const
{
    for (int i = 0; i < m_maxClients; ++i)
    {
        if (m_clientConnected[i] && m_clients[i].host == ip.host && m_clients[i].port == ip.port)
            return i;
    }
    return -1;
}

bool Server::IsClientConnected(int clientIndex) const
{
    return m_clientConnected[clientIndex];
}

const IPaddress &Server::GetClientAddress(int clientIndex) const
{
    return m_clients[clientIndex];
}

void Server::update(unsigned int delta)
{
    //Receive packages
    if (SDLNet_UDP_Recv(udpSocket, receivedPacket))
    {
        switch (*(receivedPacket->data))
        {
        case CONNECTION_REQUEST:
            std::cout << "Connection request package received\n";
            int freeIndex;
            if ((freeIndex = this->FindFreeClientIndex()) == -1)
            {   
                std::cout << "Connection denied because server full" << std::endl;
                //Send Connection denied
                this->sendConnectionDenied(receivedPacket->address);
                break;
            }
            std::cout << "Free index found: " << freeIndex << std::endl;
            int indexOfExistingClient;
            if ((indexOfExistingClient = FindExistingClientIndex(receivedPacket->address)) == -1)
            {
                std::cout << "Client doesent exist create new" << std::endl;
                m_clients[freeIndex] = receivedPacket->address;
                m_clientConnected[freeIndex] = true;
                //Send connection accepted with index
                this->sendConnectionAccepted(receivedPacket->address, freeIndex);
                m_numConnectedClients++;
                break;
            }
            else
            {
                std::cout << "Client exist sending accepted" << std::endl;
                //Send connection accepted with index
                this->sendConnectionAccepted(receivedPacket->address, indexOfExistingClient);
            }
            break;
        default:
            break;
        }
    }

    //Wait 10 sec
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);

    //Send data
    /*for(auto &value: clients) {
        value.send(this->udpSocket,*helloWorldPacket);
    }
    std::cout << "Data was send!!" << std::endl;*/
}

bool Server::sendPacket(UDPpacket &packet)
{
    // Send
    // SDLNet_UDP_Send returns number of packets sent. 0 means error
    if (SDLNet_UDP_Send(this->udpSocket, -1, &packet) == 0)
    {
        std::cout << "\tSDLNet_UDP_Send failed : " << SDLNet_GetError() << "\n"
                  << "==========================================================================================================\n";
        return false;
    }
    return true;
}

bool Server::sendConnectionDenied(IPaddress dest)
{
    this->connectionDenied->address.host = dest.host;
    this->connectionDenied->address.port = dest.port;
    return this->sendPacket(*connectionDenied);
}

bool Server::sendConnectionAccepted(IPaddress dest, int index)
{
    this->connectionAccepted->address.host = dest.host;
    this->connectionAccepted->address.port = dest.port;
    ((ConnectionAccepted *) connectionAccepted->data)->index = index;
    return this->sendPacket(*connectionAccepted);
}

Server::~Server()
{
    SDLNet_FreePacket(receivedPacket);
    SDLNet_FreePacket(connectionAccepted);
    SDLNet_UDP_Close(udpSocket);
}