#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>

#include "../include/netw_op/netw.h"
#include "../include/utils_op/utils.h"
#include "../include/netw_op/request.h"

/**
 * @brief Handle client connection
 *
 * @param fd
 */
void handle_connection(int fd)
{
    const size_t request_buffer_size = 1024;
    char request[request_buffer_size];
    char *prt;

    int bytes_recv = recv(fd, request, request_buffer_size - 1, 0);

    if (bytes_recv < 0)
    {
        err_msg("failed to receive a request from server");
        return;
    }

    // printf("\nRequest:\n%s---\n", request);

    prt = strstr(request, " HTTP/");
    if (prt == NULL)
    {
        err_msg("non-HTTP request received");
    }
    else
    {
        if (strncmp(request, "GET ", 4) == 0)
        {
            memmove(request, request + 4, strlen(request) - 3);
            handle_get_request(fd, request);
        }
        else if (strncmp(request, "POST ", 5) == 0)
        {
            memmove(request, request + 5, strlen(request) - 4);
            handle_post_request(fd, request);
        }
        else
        {
            err_msg("unknown request");
        }
    }
    close(fd);
}

/**
 * @brief Handles child process removal (to prevent zombies)
 *
 * @param signum
 */
void handle_process_termination(int signum)
{
    int status;
    pid_t pid;

    do
    {
        pid = waitpid(-1, &status, WNOHANG);
    } while (pid > 0);
}

/**
 * Main
 */
int main(int argc, char *argv[])
{
    int client_fd;
    struct sockaddr_storage cli_addr;
    char s[INET6_ADDRSTRLEN];

    char *port;

    if (argc == 2)
        port = argv[1];
    else
        port = getenv("PORT");

    if (port == NULL)
    {
        perror("Port must be specified either as an argument or environment variable \"PORT\"");
        exit(1);
    }

    int listenfd = get_listener_socket(port);
    if (listenfd < 0)
    {
        err_msg("!fatal! couldn't create listening socket");
        exit(1);
    }
    printf("Waiting for connection on port %s...\n", port);

    signal(SIGCHLD, handle_process_termination);

    while (1)
    {
        socklen_t sin_size = sizeof cli_addr;

        client_fd = accept(listenfd, (struct sockaddr *)&cli_addr, &sin_size);
        if (client_fd < 0)
        {
            err_msg("couldn't accept client connection");
            continue;
        }

        inet_ntop(cli_addr.ss_family, get_in_addr((struct sockaddr *)&cli_addr), s, sizeof s);
        printf("\nGot connection from %s\n", s);

        int pid = fork();

        if (pid < 0)
        {
            err_msg("fork failed");
            close(client_fd);
        }
        else if (pid == 0)
        {
            close(listenfd);

            handle_connection(client_fd);
            exit(0);
        }
        else
            close(client_fd);
    }

    return 0;
}
