#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <float.h>
#include <math.h>

#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAX_CONCURRENT_CONNECTIONS  10

struct sockaddr_in client;
int socket_server;
int client_descriptor;
int bFirstInput = 0;

void handleClient();
void disconnectClient();

int main(int argc, char** argv)
{
    if (argc < 2)
    {
        printf("-USAGE- %s <port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    unsigned int port = atoi(argv[1]);
    struct sockaddr_in server;
    
    if ((socket_server = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket()");
        exit(EXIT_FAILURE);
    }

    memset(&server, 0, sizeof (struct sockaddr_in));
    server.sin_addr.s_addr  = htonl(INADDR_ANY);
    server.sin_family       = AF_INET;
    server.sin_port         = htons(port);

    if (bind(socket_server, (struct sockaddr*) &server, sizeof server) == -1)
    {
        perror("bind()");
        exit(EXIT_FAILURE);
    }

    if (listen(socket_server, MAX_CONCURRENT_CONNECTIONS) == -1)
    {
        perror("listen()");
        exit(EXIT_FAILURE);
    }

    printf("The server has been successfully started...\n");

    while (1)
    {
        memset(&client, 0, sizeof client);

        int length = sizeof (client);
        client_descriptor = accept(socket_server, (struct sockaddr*) &client, &length);
             
        if (fork() == 0)
            handleClient();              

    }
    return 0;
}

void handleClient()
{
    printf("A client has connected!\n");

    char buffer[128];
    unsigned long int quantity;
    int i;

    float f = 0.0, fSum = 0.0;
    char str[128];

    while (1)
    {
        bzero(str, sizeof str);
        bzero(buffer, sizeof buffer);
        
        if (bFirstInput == 0)
        {
            if (recv(client_descriptor, &quantity, sizeof (int), 0) != -1)
            {
                quantity = ntohl(quantity);
                printf("User %d wants to enter %lu numbers!\n", client_descriptor, quantity);
                bFirstInput = 1;
            }
        }

        for (i = 0; i < quantity; i++)
        {
            if (recv(client_descriptor, buffer, sizeof buffer, 0) != -1)
            {
                signal(SIGALRM, disconnectClient);
                alarm(10);
                
                sscanf(buffer, "%f", &f);
                                
                if (ceilf(f) == f && f < 0)
                    f = 0;

                fSum += f;
            }
            else
            {
                printf("User %d has disconnected\n", client_descriptor);
                close(client_descriptor);
                exit(EXIT_FAILURE);
            }
        }
        if (fSum >= FLT_MAX)
        {
            char err2[64] = "ERROR - OUTPUT TOO LARGE: >= FLT_MAX";
            send(client_descriptor, &err2, strlen(err2) + 1, 0);
            printf("User %d has disconnected\n", client_descriptor);
            close(client_descriptor);
            exit(EXIT_FAILURE);      
        }

        snprintf(str, sizeof str, "%.4f", fSum);
        if (send(client_descriptor, str, strlen(str) + 1, 0) <= 0)
        {
            close(client_descriptor);
            perror("send()");
            exit(EXIT_FAILURE);
        }
    }
    exit(0);
}

void disconnectClient(int signal)
{
    printf("User %d has been disconnected: IDLE\n", client_descriptor);
    close(client_descriptor);
    exit(1);
}