#include "http_server.h"
#include <qobjectdefs.h>
#include <unistd.h>
#include <utils/utils.h>
#include <iostream>
#include <ofdscene.h>
#include <thread>

void HttpServer::generateRandomPort() {
    port = rand() % (65535 - 1024) + 1024;
}

HttpServer::HttpServer(QWidget *caller) : caller(caller) {
    started = false;
    port = 8080;
}

HttpServer::~HttpServer() {
    started = false;
    shutdown(serverSocket, SHUT_RDWR);
    close(serverSocket);

    for (auto &thread : clientHandlersThreads) {
        thread.join();
    }

    listenClientsThread.join();
}

int HttpServer::start()  {
    unsigned short number_of_retries = 0;

    while (number_of_retries < 10) {

        serverSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (serverSocket < 0) {
            std::cerr << "Could not obtain socket." << std::endl;
            number_of_retries ++;
            continue;
        }
        serverAddress.sin_family = AF_INET;
        serverAddress.sin_port = htons(port);
        serverAddress.sin_addr.s_addr = INADDR_ANY;

        if (bind(serverSocket, (sockaddr *)&serverAddress, sizeof(serverAddress)) < 0) {
            std::cerr << "Port " << port << " seems to be occupied. Trying to generate another one" << std::endl;
            number_of_retries ++;
            generateRandomPort();
            continue;
        }

        if (listen(serverSocket, 1) < 0) {
            std::cerr << "Could not listen port." << std::endl;
            number_of_retries ++;
            generateRandomPort();
            continue;
        }
        started = true;
        std::cout << "Listening on port: " << port << std::endl;
        listenClientsThread = std::thread(&HttpServer::acceptClients, this);
        return 0;
    }
    return -1;
}

void HttpServer::handleClient(int clientSocket) {
    std::string localIp;
    try {
        localIp = get_local_ip_address();
    } catch(std::exception e) {
        std::cerr << e.what() << std::endl;
        close(clientSocket);
        return;
    }

    char buffer[256] = {0};

    recv(clientSocket, buffer, 256, 0);

    std::string response = "HTTP/1.1 301 Moved Permanently\r\n"
                           "Content-Length: 0\r\n"
                           "Keep-Alive: timeout=5, max=100\r\n"
                           "Location: binaryeye://scan/?ret=http://" + get_local_ip_address() + ":" + std::to_string(port) + "/?result={RESULT}\r\n"
                           "\r\n";

    if (send(clientSocket, response.c_str(), response.length(), 0) < 0) {
        std::cerr << response.c_str() << std::endl;
        std::cerr << response.length() << std::endl;
        std::cerr << "Could not send message" << std::endl;
    }
    emit ((OFDScene *)caller)->httpNewMessage(QString::fromStdString(std::string(buffer)));
}

void HttpServer::acceptClients() {
    while (true) {
        if (!started) return;
        int clientSocket = accept(serverSocket, nullptr, nullptr);
        if (!started) return;

        clientHandlersThreads.push_back (
            std::thread(&HttpServer::handleClient, this, clientSocket)
        );
    }
}

unsigned short HttpServer::getPort() {
    return port;
}

bool HttpServer::isStarted() {
    return started;
}