#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_mib.h>
#include <net/if_types.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <pthread.h>
#if UDP_RECV
#include "udp_receiver.h"
#endif
#include "bpf.h"

// Configuration
#define SERIAL_PORT "/dev/cuaU0"
#define HOSTS_LOG_FILE "/tmp/hosts.log"
#define PING_TARGET "8.8.8.8"
#define INTERFACE_NAME "igb1"

// Configuration switches
// UDP_RECV is defined via compiler flag: -DUDP_RECV=1 or -DUDP_RECV=0
// DEBUG is defined via compiler flag: -DDEBUG=1 or -DDEBUG=0

// Ping configuration
#define PING_INTERVAL_SECONDS 30
#define DEFDATALEN 56
#define PING_PACKET_SIZE 64

// Color mapping definitions - SYNCED WITH ARDUINO
#define COLOR_RED      0    // Used for high connections (>2000 total)
#define COLOR_GREEN    1    // Used for high UDP (>1000)
#define COLOR_BLUE     2    // Used for high TCP (>1000)
#define COLOR_MAGENTA  3    // Used for consistent broadcast
#define COLOR_CYAN     4    // DEFAULT color
#define COLOR_YELLOW   5    // Used for high ICMP (>10)
#define COLOR_LIME     6    // Used for consistent multicast
#define COLOR_WHITE    7    // NOT USED - reserved
#define COLOR_BLACK    8    // NOT USED - reserved

// Thresholds for color changes
#define TCP_BLUE_THRESHOLD 1000      // TCP > 1000 = BLUE
#define UDP_GREEN_THRESHOLD 1000     // UDP > 1000 = GREEN
#define ICMP_YELLOW_THRESHOLD 10     // ICMP > 10 = YELLOW
#define TOTAL_CONNECTIONS_RED_THRESHOLD 2000  // TCP+UDP+ICMP > 2000 = RED

// Consistency tracking for broadcast/multicast
#define CONSISTENCY_THRESHOLD 10

#define VERSION "0.7"

// Global variables
static int serial_fd = -1;
static int running = 1;
static int ident;

// Network stats tracking
static uint64_t last_ibytes = 0;
static uint64_t last_obytes = 0;
static struct timeval last_time = {0, 0};
static int stats_initialized = 0;

// Thread-safe ping variables
static pthread_mutex_t ping_mutex = PTHREAD_MUTEX_INITIALIZER;
static int current_ping = 50;
static int ping_sequence = 0;

// Consistency tracking for broadcast/multicast
static int broadcast_history[CONSISTENCY_THRESHOLD] = {0};
static int multicast_history[CONSISTENCY_THRESHOLD] = {0};
static int history_index = 0;

// Signal handler for clean shutdown
static void signal_handler(int sig) {
    (void)sig;  // Explicitly mark as unused
    running = 0;
}

// Thread-safe ping value update
static void set_current_ping(int ping_value) {
    pthread_mutex_lock(&ping_mutex);
    current_ping = ping_value;
    pthread_mutex_unlock(&ping_mutex);
}

// Thread-safe ping value retrieval
static int get_current_ping(void) {
    int ping_value;
    pthread_mutex_lock(&ping_mutex);
    ping_value = current_ping;
    pthread_mutex_unlock(&ping_mutex);
    return ping_value;
}

// Update consistency history
static void update_consistency_history(int broadcast_active, int multicast_active) {
    broadcast_history[history_index] = broadcast_active;
    multicast_history[history_index] = multicast_active;
    history_index = (history_index + 1) % CONSISTENCY_THRESHOLD;
}

// Check if consistently active (all 1s in history)
static int is_consistently_active(int* history) {
    for (int i = 0; i < CONSISTENCY_THRESHOLD; i++) {
        if (history[i] == 0) {
            return 0;
        }
    }
    return 1;
}

// Determine color based on connections and consistency
static int determine_background_color(int tcp_count, int udp_count, int icmp_count,
                                      int broadcast_active, int multicast_active) {
    int color = COLOR_CYAN;  // Default color
    int total_connections = tcp_count + udp_count + icmp_count;
    
    // Update consistency history
    update_consistency_history(broadcast_active, multicast_active);
    
    // Check for high total connections (> 2000) - RED has highest priority
    if (total_connections > TOTAL_CONNECTIONS_RED_THRESHOLD) {
        return COLOR_RED;
    }
    
    // Check TCP threshold - BLUE (only if TCP > 1000 AND total <= 2000)
    if (tcp_count > TCP_BLUE_THRESHOLD) {
        color = COLOR_BLUE;
    }
    
    // Check UDP threshold - GREEN (only if UDP > 1000 AND total <= 2000)
    if (udp_count > UDP_GREEN_THRESHOLD) {
        color = COLOR_GREEN;
    }
    
    // Check ICMP threshold - YELLOW (only if ICMP > 10 AND total <= 2000)
    if (icmp_count > ICMP_YELLOW_THRESHOLD) {
        color = COLOR_YELLOW;
    }
    
    // Check consistency patterns (only if total <= 2000)
    if (is_consistently_active(broadcast_history)) {
        color = COLOR_MAGENTA;
    }
    
    if (is_consistently_active(multicast_history)) {
        color = COLOR_LIME;
    }
    
    // Ensure we never send WHITE (7) or BLACK (8)
    if (color == COLOR_WHITE || color == COLOR_BLACK) {
        color = COLOR_CYAN;
    }
    
    return color;
}

// ICMP checksum from FreeBSD ping
static unsigned short in_cksum(unsigned short *addr, int len) {
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1) {
        *(unsigned char *)(&answer) = *(unsigned char *)w;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}

// Dedicated ping function for the thread
static int perform_ping_thread(void) {
    int s;
    struct sockaddr_in whereto;
    struct icmp *icp;
    struct ip *ip;
    char packet[PING_PACKET_SIZE];
    char response[PING_PACKET_SIZE];
    struct timeval tv_send, tv_recv, timeout;
    int ping_result = 500;
    socklen_t fromlen;
    struct sockaddr_in from;
    ssize_t cc;
    int sequence;
    
    pthread_mutex_lock(&ping_mutex);
    sequence = ping_sequence++;
    pthread_mutex_unlock(&ping_mutex);
    
    s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (s < 0) {
        if (errno == EPERM) {
            return ping_result;
        }
        return ping_result;
    }
    
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
        close(s);
        return ping_result;
    }
    
    memset(&whereto, 0, sizeof(whereto));
    whereto.sin_family = AF_INET;
    if (inet_pton(AF_INET, PING_TARGET, &whereto.sin_addr) != 1) {
        close(s);
        return ping_result;
    }
    
    memset(packet, 0, sizeof(packet));
    icp = (struct icmp *)packet;
    icp->icmp_type = ICMP_ECHO;
    icp->icmp_code = 0;
    icp->icmp_cksum = 0;
    icp->icmp_seq = htons(sequence);
    icp->icmp_id = ident;
    
    gettimeofday(&tv_send, NULL);
    memcpy(packet + sizeof(struct icmp), &tv_send, sizeof(tv_send));
    
    int start = sizeof(struct icmp) + sizeof(tv_send);
    int end = DEFDATALEN + sizeof(struct icmp);
    if (end > PING_PACKET_SIZE) end = PING_PACKET_SIZE;
    
    for (int i = start; i < end; i++) {
        packet[i] = (i + sequence) % 256;
    }
    
    icp->icmp_cksum = in_cksum((unsigned short *)packet, DEFDATALEN + sizeof(struct icmp));
    
    if (sendto(s, packet, DEFDATALEN + sizeof(struct icmp), 0, 
               (struct sockaddr *)&whereto, sizeof(whereto)) <= 0) {
        close(s);
        return ping_result;
    }
    
    fromlen = sizeof(from);
    cc = recvfrom(s, response, sizeof(response), 0,
                 (struct sockaddr *)&from, &fromlen);
    
    if (cc <= 0) {
        close(s);
        return ping_result;
    }
    
    gettimeofday(&tv_recv, NULL);
    
    ip = (struct ip *)response;
    int hlen = ip->ip_hl << 2;
    
    if (cc < hlen + ICMP_MINLEN) {
        close(s);
        return ping_result;
    }
    
    icp = (struct icmp *)(response + hlen);
    
    if (icp->icmp_type == ICMP_ECHOREPLY &&
        icp->icmp_id == ident &&
        ntohs(icp->icmp_seq) == sequence) {
        
        long sec = tv_recv.tv_sec - tv_send.tv_sec;
        long usec = tv_recv.tv_usec - tv_send.tv_usec;
        ping_result = (sec * 1000) + (usec / 1000);
        
        if (ping_result > 500) ping_result = 500;
    }
    
    close(s);
    return ping_result;
}

// Background ping thread function
static void* ping_thread(void* arg) {
    (void)arg;  // Explicitly mark as unused
    while (running) {
        int ping_result = perform_ping_thread();
        set_current_ping(ping_result);
        
        for (int i = 0; i < PING_INTERVAL_SECONDS && running; i++) {
            sleep(1);
        }
    }
    return NULL;
}

// Count connections - using the most compatible approach
static int count_connections(int *tcp_count, int *udp_count, int *icmp_count) {
    FILE *fp;
    char line[1024];
    
    // Use pfctl command for now - we'll optimize this later if needed
    fp = popen("pfctl -ss 2>/dev/null", "r");
    if (!fp) {
        return -1;
    }
    
    // Initialize counters
    *tcp_count = 0;
    *udp_count = 0;
    *icmp_count = 0;
    
    while (fgets(line, sizeof(line), fp) != NULL) {
        // Skip localhost and LAN addresses in the line
        if (strstr(line, "127.0.0.1")) {
            continue;
        }
        
        // Count by protocol
        if (strstr(line, "tcp") || strstr(line, "TCP")) {
            (*tcp_count)++;
        } else if (strstr(line, "udp") || strstr(line, "UDP")) {
            (*udp_count)++;
        } else if (strstr(line, "icmp") || strstr(line, "ICMP")) {
            (*icmp_count)++;
        }
    }
    
    pclose(fp);
    return 0;
}

// Get interface statistics - SIMPLE DIRECT APPROACH
static int get_interface_stats(uint64_t *ibytes, uint64_t *obytes) {
    int mib[6];
    size_t len;
    struct ifmibdata ifmd;
    
    // Get interface index directly
    int ifindex = if_nametoindex(INTERFACE_NAME);
    if (ifindex == 0) {
        fprintf(stderr, "Interface %s not found\n", INTERFACE_NAME);
        return -1;
    }
    
    // Set up the mib for getting interface data
    mib[0] = CTL_NET;
    mib[1] = PF_LINK;
    mib[2] = NETLINK_GENERIC;
    mib[3] = IFMIB_IFDATA;
    mib[4] = ifindex;
    mib[5] = IFDATA_GENERAL;
    
    // Get the statistics for our interface
    len = sizeof(ifmd);
    if (sysctl(mib, 6, &ifmd, &len, NULL, 0) < 0) {
        perror("sysctl ifdata");
        return -1;
    }
    
    *ibytes = ifmd.ifmd_data.ifi_ibytes;
    *obytes = ifmd.ifmd_data.ifi_obytes;
    
    return 0;
}

// Calculate network speeds in Mbps
static int get_network_stats(float *download, float *upload) {
    uint64_t current_ibytes, current_obytes;
    struct timeval current_time;
    
    // Get current interface statistics
    if (get_interface_stats(&current_ibytes, &current_obytes) != 0) {
        return -1;
    }
    
    // Get current time
    gettimeofday(&current_time, NULL);
    
    // If first call, just store values and return 0
    if (!stats_initialized) {
        last_ibytes = current_ibytes;
        last_obytes = current_obytes;
        last_time = current_time;
        stats_initialized = 1;
        *download = 0.0f;
        *upload = 0.0f;
        return 0;
    }
    
    // Calculate time difference in seconds
    double time_diff = (current_time.tv_sec - last_time.tv_sec) + 
                      (current_time.tv_usec - last_time.tv_usec) / 1000000.0;
    
    // Ensure reasonable time difference
    if (time_diff < 0.5) {
        time_diff = 1.0;
    }
    
    // Calculate Mbps
    if (current_ibytes >= last_ibytes) {
        uint64_t bytes_diff = current_ibytes - last_ibytes;
        *download = (float)((bytes_diff * 8.0) / (time_diff * 1000000.0));
    } else {
        *download = 0.0f;
    }
    
    if (current_obytes >= last_obytes) {
        uint64_t bytes_diff = current_obytes - last_obytes;
        *upload = (float)((bytes_diff * 8.0) / (time_diff * 1000000.0));
    } else {
        *upload = 0.0f;
    }
    
    // Update stored values
    last_ibytes = current_ibytes;
    last_obytes = current_obytes;
    last_time = current_time;
    
    return 0;
}

// Print status in Debian service style
static void print_status(const char *message, const char *status) {
    printf(" * %-45s [", message);
    if (strcmp(status, "ok") == 0) {
        printf("\033[32m OK \033[0m]\n");
    } else if (strcmp(status, "disabled") == 0) {
        printf("\033[31m DIS \033[0m]\n");
    } else {
        printf(" %s ]\n", status);
    }
}

// Proper serial initialization for Arduino
static int init_serial(const char *port) {
    struct termios tty;
    
    print_status("Opening serial port", "ok");
    serial_fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial_fd < 0) {
        perror("open serial port");
        return -1;
    }
    
    fcntl(serial_fd, F_SETFL, 0);
    
    if (tcgetattr(serial_fd, &tty) != 0) {
        perror("tcgetattr");
        close(serial_fd);
        serial_fd = -1;
        return -1;
    }
    
    cfsetospeed(&tty, B19200);
    cfsetispeed(&tty, B19200);
    
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~CRTSCTS;
    tty.c_cflag |= (CREAD | CLOCAL);
    
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
    tty.c_oflag &= ~OPOST;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
    
    tty.c_cc[VTIME] = 0;
    tty.c_cc[VMIN] = 0;
    
    if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) {
        perror("tcsetattr");
        close(serial_fd);
        serial_fd = -1;
        return -1;
    }
    
    tcflush(serial_fd, TCIOFLUSH);
    
    print_status("Serial port configured", "ok");
    
    int dtr_flag = 0;
    ioctl(serial_fd, TIOCMGET, &dtr_flag);
    dtr_flag &= ~TIOCM_DTR;
    ioctl(serial_fd, TIOCMSET, &dtr_flag);
    usleep(250000);
    
    dtr_flag |= TIOCM_DTR;
    ioctl(serial_fd, TIOCMSET, &dtr_flag);
    usleep(250000);
    
    print_status("Waiting for Arduino boot", "ok");
    sleep(3);
    
    tcflush(serial_fd, TCIFLUSH);
    
    print_status("Synchronizing with Arduino", "ok");
    write(serial_fd, "\n", 1);
    tcdrain(serial_fd);
    usleep(100000);
    
    print_status("Waiting for Arduino boot animation", "ok");
    sleep(5);
    
    tcflush(serial_fd, TCIFLUSH);
    
    return 0;
}

// Read file content (single line)
static int read_file(const char *filename, char *buffer, size_t size) {
    FILE *file = fopen(filename, "r");
    if (!file) return -1;
    
    if (fgets(buffer, size, file) == NULL) {
        fclose(file);
        return -1;
    }
    
    fclose(file);
    
    buffer[strcspn(buffer, "\n")] = 0;
    return 0;
}

// Ping to LED mapping (10ms to 500ms, 10 LEDs)
static int map_ping_to_leds(int ping_value) {
    const int min_ping = 10;
    const int max_ping = 500;
    const int max_leds = 10;
    
    if (ping_value < min_ping) ping_value = min_ping;
    if (ping_value > max_ping) ping_value = max_ping;
    
    int led_count = (ping_value - min_ping) * max_leds / (max_ping - min_ping);
    
    if (led_count < 0) led_count = 0;
    if (led_count > max_leds) led_count = max_leds;
    
    return led_count;
}

// Get color name for display
static const char *get_color_name(int color) {
    switch (color) {
        case COLOR_RED:     return "RED";
        case COLOR_GREEN:   return "GREEN";
        case COLOR_BLUE:    return "BLUE";
        case COLOR_MAGENTA: return "MAGENTA";
        case COLOR_CYAN:    return "CYAN";
        case COLOR_YELLOW:  return "YELLOW";
        case COLOR_LIME:    return "LIME";
        case COLOR_WHITE:   return "WHITE (NOT USED)";
        case COLOR_BLACK:   return "BLACK (NOT USED)";
        default:            return "UNKNOWN";
    }
}

// Send data to Arduino - UPDATED WITH NEW FORMAT INCLUDING HOSTS 1-3 AND HOSTS COUNT
static void send_to_arduino(float download, float upload, int hosts_count, int icmp_count,
                            int tcp, int udp, int color, int ping_leds, 
                            int multicast_led, int broadcast_led, int nonip_led, int ipx_led) {
    char buffer[256];
    
    // Start with host 0 data in NEW FORMAT: h0:rx:tx:tcp:udp:icmp,hosts_count,color,ping_leds,multicast_led,broadcast_led,nonip_led,ipx_led
    int len = snprintf(buffer, sizeof(buffer), 
                      "h0:%.2f:%.2f:%d:%d:%d,%d,%d,%d,%d,%d,%d,%d",
                      download, upload, tcp, udp, icmp_count,
                      hosts_count, color, ping_leds, multicast_led, broadcast_led, nonip_led, ipx_led);
    
#if UDP_RECV
    // Check for host 1, 2, 3 data from UDP receiver
    for (int i = 0; i < 3; i++) {
        HostData host_data_temp;
        if (get_host_data(i+1, &host_data_temp) == 0) {
            // Check if data is not all zeros (has been updated)
            if (host_data_temp.rx > 0 || host_data_temp.tx > 0 || 
                host_data_temp.tcp > 0 || host_data_temp.udp > 0 || 
                host_data_temp.icmp > 0) {
                
                // Add host data to the buffer in format: hX:rx:tx:tcp:udp:icmp
                len += snprintf(buffer + len, sizeof(buffer) - (size_t)len,
                              ",h%d:%.2f:%.2f:%ld:%ld:%ld",
                              i+1, 
                              host_data_temp.rx, host_data_temp.tx,
                              host_data_temp.tcp, host_data_temp.udp, 
                              host_data_temp.icmp);
            }
        }
    }
#endif
    
    // Add newline
    if ((size_t)len < sizeof(buffer) - 2) {
        buffer[len++] = '\n';
        buffer[len] = '\0';
    }
    
    if (write(serial_fd, buffer, (size_t)len) != (ssize_t)len) {
        perror("write to serial");
    }
    
    tcdrain(serial_fd);
    
#if DEBUG
    // Show what we sent (without newline for clean output)
    printf("SENT: %s", buffer);
#endif
}

int main(void) {
    float download, upload;
    int hosts_count = 0;
    int tcp_count, udp_count, icmp_count;
    int multicast_active, broadcast_active, nonip_active, ipx_active;
    char hosts_value[32];
    int iteration = 0;
    pthread_t ping_thread_id;
#if UDP_RECV
    pthread_t udp_thread_id;
#endif
    
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    // Clean banner with text positioned closer to the tree
    printf("\n");
    printf("\033[32m            \\/ |    |/\033[0m\n");
    printf("\033[32m        \\/ / \\||/  /_/___/_\033[0m\n");
    printf("\033[32m         \\/   |/ \\/\033[0m\n");
    printf("\033[32m    _\\__\\_\\   |  /_____/_\033[0m\n");
    printf("\033[32m           \\  | /          /\033[0m\n");
    printf("\033[32m  __ _-----`  |{,-----------~\033[0m\n");
    printf("            \\ }{\n");
    printf("             }{{\n");
    printf("             }}{\n");
    printf("             {{}\n");
    printf("       , -=-~{ .-^- _\n");
    printf("              `}              MultiCast Tree\n");
    printf("              {               Version: %s\n", VERSION);
    printf("                              by NLD\n");
    printf("\n");
    
    ident = getpid() & 0xFFFF;
    
    if (geteuid() != 0) {
        printf("ERROR: This program must be run as root for ICMP ping and BPF to work!\n");
        printf("Please run: sudo ./multicast\n");
        return 1;
    }
    
#if !DEBUG
    if (daemon(0, 0) < 0) {
        perror("daemon");
        return 1;
    }
#endif
    
    // Initialize consistency history
    memset(broadcast_history, 0, sizeof(broadcast_history));
    memset(multicast_history, 0, sizeof(multicast_history));
    history_index = 0;
    
    if (pthread_create(&ping_thread_id, NULL, ping_thread, NULL) != 0) {
        fprintf(stderr, "ERROR: Failed to create ping thread\n");
        return 1;
    }
    print_status("Background ping thread started", "ok");
    
#if UDP_RECV
    start_udp_receiver(&udp_thread_id);
    print_status("UDP receiver thread started", "ok");
#else
    print_status("UDP receiver", "disabled");
#endif
    
    if (init_serial(SERIAL_PORT) != 0) {
        fprintf(stderr, "Failed to initialize serial port\n");
        running = 0;
        pthread_join(ping_thread_id, NULL);
#if UDP_RECV
        stop_udp_receiver(udp_thread_id);
#endif
        return 1;
    }
    
    print_status("Arduino ready", "ok");
    print_status("Starting monitoring loop", "ok");
    printf("\n");
    
    while (running) {
        iteration++;
        
        if (get_network_stats(&download, &upload) != 0) {
            fprintf(stderr, "Failed to get network stats\n");
            download = 0.0f;
            upload = 0.0f;
        }
        
        // Use simplified connection counting
        if (count_connections(&tcp_count, &udp_count, &icmp_count) != 0) {
            tcp_count = udp_count = icmp_count = 0;
        }
        
        // Use BPF-based packet detection for accurate results
        if (get_packet_stats_bpf(&multicast_active, &broadcast_active, &nonip_active, &ipx_active) != 0) {
            multicast_active = 0;
            broadcast_active = 0;
            nonip_active = 0;
            ipx_active = 0;
        }
        
        if (read_file(HOSTS_LOG_FILE, hosts_value, sizeof(hosts_value)) == 0) {
            int new_hosts = atoi(hosts_value);
            if (new_hosts > 0) {
                hosts_count = new_hosts;
            }
        }
        
        // Get ping value from thread-safe storage
        int current_ping_value = get_current_ping();
        
        int led_count = map_ping_to_leds(current_ping_value);
        
        // Determine background color based on connections and consistency
        int bg_color = determine_background_color(tcp_count, udp_count, icmp_count,
                                                  broadcast_active, multicast_active);
        
        // Send ALL data to Arduino with NEW FORMAT including hosts 1-3 AND hosts_count
        send_to_arduino(download, upload, hosts_count, icmp_count,
                       tcp_count, udp_count, bg_color, led_count, 
                       multicast_active, broadcast_active, nonip_active, ipx_active);
        
#if DEBUG
        // Calculate total connections for debug output
        int total_connections = tcp_count + udp_count + icmp_count;
        
        // Show host 0 data with color reasoning
        printf("HOST 0 => Down: %.2f | Up: %.2f | Hosts: %d | ICMP: %d | "
               "TCP: %d | UDP: %d | Total: %d | Ping: %dms -> LEDs: %d | Color: %s(%d) | "
               "Multicast: %d | Broadcast: %d | Non-IP: %d | IPX: %d\n",
               download, upload, hosts_count, icmp_count, tcp_count, udp_count,
               total_connections, current_ping_value, led_count, get_color_name(bg_color), bg_color,
               multicast_active, broadcast_active, nonip_active, ipx_active);
        
        // Show consistency status
        int broadcast_consistency = is_consistently_active(broadcast_history);
        int multicast_consistency = is_consistently_active(multicast_history);
        printf("Consistency => Broadcast: %d/%d | Multicast: %d/%d\n",
               broadcast_consistency, CONSISTENCY_THRESHOLD,
               multicast_consistency, CONSISTENCY_THRESHOLD);
#endif
        
        sleep(1);
    }
    
    printf("\nShutting down...\n");
    
    pthread_join(ping_thread_id, NULL);
#if UDP_RECV
    stop_udp_receiver(udp_thread_id);
#endif
    pthread_mutex_destroy(&ping_mutex);
    
    if (serial_fd >= 0) {
        close(serial_fd);
    }
    
    return 0;
}
