#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <stdbool.h>
#include <sys/time.h>

#define SERVER_PORT 11592
#define MAX_SERVERS 10
#define COMMAND_LENGTH 25

typedef struct {
    struct sockaddr_in address;
    struct timeval last_request;
    bool is_waiting;
} ServerInfo;

ServerInfo server_infos[MAX_SERVERS];
int num_servers = 0;

int main() {
    int sockfd;
    struct sockaddr_in client_addr;
    char buf[256];
    socklen_t len = sizeof(client_addr);
    struct timeval start_time, current_time;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&client_addr, 0, sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(SERVER_PORT);
    client_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (const struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    while (1) {
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(sockfd, &read_fds);
        struct timeval timeout;
        timeout.tv_sec = 3 * 1275 / 1000;
        timeout.tv_usec = (3 * 1275 % 1000) * 1000;

        if (select(sockfd + 1, &read_fds, NULL, NULL, &timeout) > 0) {
            int n = recvfrom(sockfd, buf, 256, 0, (struct sockaddr*)&client_addr, &len);
            buf[n] = '\0';

            if (strncmp(buf, "RESP:", 5) == 0) {
                for (int i = 0; i < num_servers; i++) {
                    if (memcmp(&client_addr, &server_infos[i].address, sizeof(struct sockaddr_in)) == 0) {
			server_infos[i].is_waiting = false;
			gettimeofday(&current_time, NULL);
			long last_request_ms = (server_infos[i].last_request.tv_sec * 1000) + (server_infos[i].last_request.tv_usec / 1000);
			long current_time_ms = (current_time.tv_sec * 1000 + current_time.tv_usec / 1000);
                        time_t current_time;
    			struct tm *time_info;
			time(&current_time);
			time_info = localtime(&current_time);
			int year = time_info->tm_year + 1900;
			int month = time_info->tm_mon + 1;
			int day = time_info->tm_mday;
			int hour = time_info->tm_hour;
			int minute = time_info->tm_min;
			int second = time_info->tm_sec;
			printf("Current date and time: %04d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);
			printf("Received response after %ld ms: %s\n", current_time_ms - last_request_ms, buf+5);
			break;
                    }
                }
            } else if (strncmp(buf, "HELLO:", 6) == 0) {
                if (num_servers < MAX_SERVERS) {
                    server_infos[num_servers].address = client_addr;
                    gettimeofday(&server_infos[num_servers].last_request, NULL);
		    server_infos[num_servers].is_waiting = false;
                    num_servers++;
                }
                printf("Registered server: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
            }
        }

        gettimeofday(&current_time, NULL);
        for (int i = 0; i < num_servers; i++) {
            if (((current_time.tv_sec - server_infos[i].last_request.tv_sec) * 1000 + (current_time.tv_usec - server_infos[i].last_request.tv_usec) / 1000 > 3 * 1275) && (server_infos[i].is_waiting)) {
                                
                printf("Server timeout - removed from list: %s:%d\n", inet_ntoa(server_infos[i].address.sin_addr), ntohs(server_infos[i].address.sin_port));
                for (int j = i; j < num_servers - 1; j++) {
                    server_infos[j] = server_infos[j + 1];
                }
                num_servers--;
                i--;
            }
        }

	usleep((rand() % 1276 + 100) * 1000);
        if (num_servers > 0) {
            int selected_server = rand() % num_servers;
            char command[COMMAND_LENGTH + 5]="CMD:";
            for (int i = 4; i < COMMAND_LENGTH+4; i++) {
                char chars[] = "abcdef0123456789";
                command[i] = chars[rand() % (sizeof(chars) - 1)];
            }
            command[COMMAND_LENGTH+4] = '\0';
            sendto(sockfd, command, COMMAND_LENGTH, 0, (struct sockaddr*)&server_infos[selected_server].address, len);
	    server_infos[selected_server].is_waiting = true;
	    gettimeofday(&server_infos[selected_server].last_request, NULL);
            printf("Sent command to server: %s:%d\n", inet_ntoa(server_infos[selected_server].address.sin_addr), ntohs(server_infos[selected_server].address.sin_port));
        }

    }

    return 0;
}
