#include <iostream>
#include <cstring>
#include <chrono>
#include <mutex>
#include <thread>
#include <vector>

#include <poll.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <unistd.h>

#include "server/GameServer.h"
#include "server/network/Server.h"
#include "server/commands/ServerCommands.h"
#include "server/commands/CommandManager.h"

static ServerCommands serverCommands;

static const std::chrono::nanoseconds NANOS_PER_TICK = std::chrono::nanoseconds(50000000);
static const std::chrono::nanoseconds MIN_NANO_SLEEP = std::chrono::nanoseconds(300000);

static std::vector<std::string> commandQueue;
static std::mutex comandQueueMutex;

static void init()
{
    rl_bind_key('\t', rl_insert);
    rl_event_hook = []() 
    {
        if(!serverCommands.isRunning())
        {
            rl_stuff_char('\n');
        }
        return 0;
    };
}

static void readFromConsole()
{
    while(serverCommands.isRunning())
    {
        char* test = readline("> ");
        if(test == nullptr)
        {
            continue;
        }
        std::string command(test);
        if(command.size() != 0)
        {
            add_history(test);
            std::lock_guard<std::mutex> lg(comandQueueMutex);
            commandQueue.push_back(command);
        }
        free(test);
    }
}

static void handleCommandQueue()
{
    std::lock_guard<std::mutex> lg(comandQueueMutex);
    for(std::string& s : commandQueue)
    {
        CommandManager::execute(serverCommands, s);
    }
    commandQueue.clear();
}

static void tick()
{
    rl_clear_visible_line();
    handleCommandQueue();
    std::cout << "tick\n";
    rl_forced_update_display();
}

static void onFullServerClientConnect(int socket)
{
    Stream answer;
    answer.write("Sorry, the server is full");
    answer.sendToSocket(socket);
}

static void onClientConnect(int socket)
{
    std::cout << socket << " has connected\n";

    Stream answer;
    answer.write("Welcome to the server.");
    answer.sendToSocket(socket);
}

static void onClientPackage(int socket, Stream& in)
{
    std::string s = "";
    while(in.hasData())
    {
        char c;
        in.read(&c, 1);
        s = c + s;
    }

    Stream answer;
    answer.write(s.data(), s.length());
    answer.sendToSocket(socket);
}

static void onClientDisconnect(int socket)
{
    std::cout << socket << " has disconnected\n";

    Stream answer;
    answer.write("Bye.");
    answer.sendToSocket(socket);
}

static void loop()
{
    auto lastTime = std::chrono::steady_clock::now().time_since_epoch();
    auto lag = lastTime.zero();

    std::thread commandThread = std::thread(readFromConsole);
    while(serverCommands.isRunning())
    {
        auto time = std::chrono::steady_clock::now().time_since_epoch();
        lag += time - lastTime;
        lastTime = time;
        while(lag >= NANOS_PER_TICK)
        {
            lag -= NANOS_PER_TICK;
            tick();
        }

        auto waitTime = NANOS_PER_TICK - lag - MIN_NANO_SLEEP;
        if(waitTime > MIN_NANO_SLEEP)
        {
            std::this_thread::sleep_for(waitTime);
        }
    }
    commandThread.join();
}

void GameServer::start(u16 port, u16 maxClients)
{
    init();
    Server::setFullServerClientConnectFunction(onFullServerClientConnect);
    Server::setClientConnectFunction(onClientConnect);
    Server::setClientPackageFunction(onClientPackage);
    Server::setClientDisconnectFunction(onClientDisconnect);
    if(!Server::start(port, maxClients))
    {
        return;
    }
    loop();
}