#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "freertos/timers.h"
#include "soc/soc.h"
#include "soc/gpio_struct.h"
#include "soc/gpio_reg.h"
#include "esp32/rom/ets_sys.h"
#include "soc/rtc.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "esp_transport.h"
#include "esp_transport_tcp.h"
#include "esp_transport_socks_proxy.h"
#include <sys/time.h>
#include <errno.h>
#include <arpa/inet.h>
#include <math.h>
#include "ethernet_init.h"
#include "sdkconfig.h"
#include "lwip/sockets.h"
#include "lwip/inet.h"
#include <netinet/in.h>
#include <fcntl.h>

#define BROADCAST_PORT 8000
#define MESSAGE "Scintilator"
#define MAX_MESSAGE_LEN 128
#define TCP_TARGET_LEN 16
#ifdef CONFIG_EXAMPLE_ENABLE_PROXY
#define PROXY_ADDR CONFIG_EXAMPLE_PROXY_ADDR
#define PROXY_PORT CONFIG_EXAMPLE_PROXY_PORT
#endif
#define MAX_RETRY_COUNT 5
#define MAX_POLL_TIMEOUT_ERRORS 5
#define INTERRUPT_INPUT 32
#define GPIO_CLOCK 33
#define NUM_GPIO_PINS 12
#define NUM_CYCLES 32
#define BIT_1 14
#define BIT_2 13
#define BIT_3 15
#define BIT_4 4
#define BIT_5 16
#define BIT_6 17
#define BIT_7 5
#define BIT_8 18
#define BIT_9 19
#define BIT_10 21
#define BIT_11 12
#define BIT_12 2
#define MAX_BROADCAST_ADDR_LEN 16 

uint64_t get_time_us() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}

esp_netif_t *eth_netif = NULL; // Declare eth_netif globally
static const char *TAG = "tcp_transport_client";
esp_transport_handle_t transport = NULL;
static bool is_connected = false;
static int unsuccessful_retries = 0;
int sock;
static int consecutive_poll_timeout_errors = 0;
static bool received_from_server = false; 
char global_broadcast_address_str[MAX_BROADCAST_ADDR_LEN];
bool trigger = false;
bool protocol_transition = false;
uint32_t data_array[NUM_CYCLES] = {0};
char TCP_TARGET[TCP_TARGET_LEN];
/*
static const gpio_num_t GPIO_PINS[NUM_GPIO_PINS] = {
    BIT_1, BIT_2, BIT_3, BIT_4,
    BIT_5, BIT_6, BIT_7, BIT_8,
    BIT_9, BIT_10, BIT_11, BIT_12
};
*/
TaskHandle_t tcpTaskHandle;
SemaphoreHandle_t tcpMutex;
SemaphoreHandle_t dataReceivedSemaphore;
QueueHandle_t tcpDataQueue;
TimerHandle_t timerHandle;
TimerHandle_t DHCP_timer_handle;

int counter = 0; 
bool global_digit = false; 
int received_integer = 0;
bool timer_started = false;

void establish_connection();
void disconnect_if_connected();
void Impulse_Count();
void close_connection();
void send_data_over_tcp(uint32_t *data_array, size_t num_elements);
void send_data_task(void *pvParameters);
void receive_data_task(void *pvParameters);
static void IRAM_ATTR gpio_interrupt_handler(void *args);

static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data){
    uint8_t mac_addr[6] = {0};
    esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
    switch (event_id) {
    case ETHERNET_EVENT_CONNECTED:
        esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
        ESP_LOGI(TAG, "Ethernet Link Up");
        ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
                 mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
        ESP_LOGI(TAG, "Waiting 60 seconds to obtain IP address from DHCP");
        xTimerStart(DHCP_timer_handle, 0);
                 vTaskDelay(10 / portTICK_PERIOD_MS);
        break;
    case ETHERNET_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "Ethernet Link Down");
        break;
    case ETHERNET_EVENT_START:
        ESP_LOGI(TAG, "Ethernet Started");
        break;
    case ETHERNET_EVENT_STOP:
        ESP_LOGI(TAG, "Ethernet Stopped");
        break;
    default:
        break;
    }
}

void DHCP_timer_callback(TimerHandle_t xTimer) {
    if(strncmp(TCP_TARGET, "169", 3) == 0){
    ESP_LOGE(TAG, "Did not obtain IP address from DHCP in 60 seconds. Executing software reset...");
    esp_restart();    
    }
    /*
    else{
        xTimerStop(DHCP_timer_handle, 0);
        ESP_LOGI(TAG, "Dynamically allocated IP address acquired");
        return;
    }
    */
}

void udp_client_send_message(char *ip, int dest_port, char *message) {
    int addr_family = AF_INET;
    int ip_protocol = IPPROTO_IP;
    struct sockaddr_in dest_addr;
    dest_addr.sin_addr.s_addr = inet_addr(ip);
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(dest_port);
    sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        return;
    }
    ESP_LOGI(TAG, "Socket created, sending to %s:%d", ip, dest_port);
    int err = sendto(sock, message, strlen(message), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err < 0) {
        ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
    } else {
        ESP_LOGI(TAG, "Message sent: %s", message);
    }
    /*
    if (sock != -1) {
        shutdown(sock, 0);
        close(sock);
        ESP_LOGI(TAG, "UDP socket closed");
    }
    */
}

static void got_ip_event_handler(void *arg, esp_event_base_t event_base,int32_t event_id, void *event_data){
    
    vTaskDelay(10 / portTICK_PERIOD_MS);

    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    if (!event) {
        ESP_LOGE(TAG, "Event data is NULL");
        printf("Could not retrieve network parameters");
        return;
    }
    bool server_reception = false;
    char ip[16];
    char subnet_mask[16];
    inet_ntoa_r(event->ip_info.ip, ip, sizeof(ip));
    inet_ntoa_r(event->ip_info.netmask, subnet_mask, sizeof(subnet_mask));
    struct in_addr addr;
    inet_aton(ip, &addr);
    struct in_addr subnet;
    inet_aton(subnet_mask, &subnet);
    struct in_addr network_address;
    network_address.s_addr = addr.s_addr & subnet.s_addr;
    struct in_addr broadcast_address;
    broadcast_address.s_addr = network_address.s_addr | ~subnet.s_addr;
    snprintf(global_broadcast_address_str, MAX_BROADCAST_ADDR_LEN, "%lu.%lu.%lu.%lu",
             (broadcast_address.s_addr >> 24) & 0xFF,
             (broadcast_address.s_addr >> 16) & 0xFF,
             (broadcast_address.s_addr >> 8) & 0xFF,
             broadcast_address.s_addr & 0xFF);

    if(strncmp(ip, "169", 3) == 0){
        ESP_LOGI(TAG, "Automatically configured IP address acquired, waiting for DHCP address");
        return;
    }
    else{
        xTimerStop(DHCP_timer_handle, 0);
        ESP_LOGI(TAG, "Dynamically allocated IP address acquired");
    }

    printf("Network Address: %s\n", inet_ntoa(network_address));
    printf("Broadcast Address: %s\n", inet_ntoa(broadcast_address));

    vTaskDelay(10 / portTICK_PERIOD_MS);

    udp_client_send_message(inet_ntoa(broadcast_address),BROADCAST_PORT,MESSAGE);
    //int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    //if (sock < 0) {
      //  ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        //return;
    //}
    //else{
      //  ESP_LOGI(TAG, "TCP socket created");
    
    //}
    struct sockaddr_in client_addr;
    client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(BROADCAST_PORT);
    if (bind(sock, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) {
        ESP_LOGE(TAG, "Socket bind failed: errno %d", errno);
        return;
    }

    char rx_buffer[MAX_MESSAGE_LEN];
    struct sockaddr_in source_addr; 
    socklen_t socklen = sizeof(source_addr);
    
    while (!server_reception) {
        int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
        if (len < 0) {
            ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
            break;
        } else {
            rx_buffer[len] = 0; 
            ESP_LOGI(TAG, "Message received: Scintilator. From %s:%d",inet_ntoa(source_addr.sin_addr), ntohs(source_addr.sin_port));
            snprintf(TCP_TARGET, TCP_TARGET_LEN, "%s", inet_ntoa(source_addr.sin_addr));
            server_reception = true; 
        }
    }
    if (sock != -1) {
        shutdown(sock, 0);
        close(sock);
        ESP_LOGI(TAG, "UDP socket closed");
    }
    protocol_transition = true;
}

void timerCallback(TimerHandle_t xTimer) {
    gpio_intr_disable(GPIO_NUM_32);
    vTaskDelay(35 / portTICK_PERIOD_MS); 
    ESP_LOGI(TAG, "Timer expired. Disconnecting from server...");
    vTaskDelay(5000 / portTICK_PERIOD_MS);  
    close_connection();
    ESP_LOGI(TAG, "Interrupt count: %d", counter);
}
void Impulse_Count(){
    gpio_intr_disable(GPIO_NUM_32);//-----
    vTaskDelay(35 / portTICK_PERIOD_MS); 
    ESP_LOGI(TAG, "Impulse target was reached. Disconnecting from server...");
    vTaskDelay(5000 / portTICK_PERIOD_MS);  
    close_connection();
    ESP_LOGI(TAG, "Interrupt count: %d", (counter+1)); 
}
float ntohf(float netfloat) {
    union {
        uint32_t n;
        float f;
    } tmp;
    tmp.n = htonl(*(uint32_t *)&netfloat);
    global_digit = (int)tmp.f & 1;
    int intPart = (int)tmp.f;
    float fracPart = tmp.f - intPart;
    if (intPart != 0) {
        intPart >>= 1; 
    } else {
        fracPart /= 2.0;
    }
    tmp.f = intPart + fracPart;
    return tmp.f;
}


void establish_connection() {
    if (transport == NULL) {
        ESP_LOGE(TAG, "Error: TCP transport not initialized");
        return;
    }
/*
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to create TCP socket: errno %d", errno);
        return;
    }
    
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(BROADCAST_PORT); // Use the appropriate port number

    // Convert the TCP target IP address from string to binary form
    if (inet_pton(AF_INET, TCP_TARGET, &server_addr.sin_addr) <= 0) {
        ESP_LOGE(TAG, "Invalid address: errno %d", errno);
        close(sock);
        return;
    }
*/

    //int optval = 1;
    //setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
    
    const int connection_timings[MAX_RETRY_COUNT] = {1, 1, 1, 2, 3, 3, 5, 5, 5, 0}; 
    for (int i = 0; i < MAX_RETRY_COUNT; i++) {
        if (!is_connected) {
            vTaskDelay(connection_timings[i] * 1000 / portTICK_PERIOD_MS); 
            int err = esp_transport_connect(transport, /*global_broadcast_address_str"10.10.10.101"*/TCP_TARGET, BROADCAST_PORT, 30000); 
                ESP_LOGI(TAG, "Connected successfully to %s:%d", TCP_TARGET, BROADCAST_PORT);
                is_connected = true; 
                break; 
            } else {
                ESP_LOGE(TAG, "Connection failed: errno %d", errno);
            }
        }
    }
void disconnect_if_connected() {
    /*
    if (is_connected) {
        int err = esp_transport_close(transport);
        if (err == 0) {
            ESP_LOGI(TAG, "Closed the connection successfully");
        } else {
            ESP_LOGE(TAG, "Error closing the connection: errno %d", errno);
        }
        is_connected = false;  
    }
    */
    if (is_connected) {
        // Close the transport layer connection
        int err = esp_transport_close(transport);
        if (err == 0) {
            ESP_LOGI(TAG, "Closed the transport connection successfully");
        } else {
            ESP_LOGE(TAG, "Error closing the transport connection: errno %d", errno);
        }
        int socket_fd = esp_transport_get_errno(transport);
        // Close the socket if applicable
        if (socket_fd >= 0) {  // Assuming socket_fd is the socket file descriptor
            int socket_err = close(socket_fd);
            if (socket_err == 0) {
                ESP_LOGI(TAG, "Socket closed successfully");
            } else {
                ESP_LOGE(TAG, "Error closing the socket: errno %d", errno);
            }
            socket_fd = -1;  // Reset the socket file descriptor
        }

        is_connected = false;  // Mark as disconnected
    }
   
}
void close_connection() {
    disconnect_if_connected();
}
void send_data_over_tcp(uint32_t *data_array, size_t num_elements) {
    
    if (transport == NULL) {
        ESP_LOGE(TAG, "Error: TCP transport not initialized");
        return;
    }
    if (!is_connected || !received_from_server) { 
        ESP_LOGE(TAG, "Error: Not connected to server or message not received from server");
        return;
    }
    
    size_t total_bytes_sent = 0;
    size_t remaining_elements = num_elements;
    uint8_t *data_ptr = (uint8_t *)data_array;
    int retry_count = 0;
    bool retry_exceeded = false;
    while (remaining_elements > 0 && !retry_exceeded) {
        int bytes_written = esp_transport_write(transport, (char *)data_ptr, sizeof(uint32_t) * remaining_elements,250);
        
        if (bytes_written < 0) {
            int error_code = errno;
            ESP_LOGE(TAG, "Error occurred during sending: esp_transport_write() returned %d, errno %d", bytes_written, error_code);
            if (error_code == ECONNRESET) {
                ESP_LOGE(TAG, "Connection reset by peer. Attempting to reconnect...");
                is_connected = false;
                esp_transport_close(transport);
            } else {
                ESP_LOGE(TAG, "Unhandled error. Closing the transport.");
                esp_transport_close(transport);
            }
            consecutive_poll_timeout_errors++;  
            //vTaskDelay(1000 / portTICK_PERIOD_MS);  
            continue;  
        }
        
        
        if (bytes_written == 0) {
            //ESP_LOGW(TAG, "Zero bytes written to TCP. Retrying...");
            vTaskDelay(500 / portTICK_PERIOD_MS);  
            retry_count++;
            if (retry_count >= MAX_RETRY_COUNT) {
                ESP_LOGE(TAG, "Maximum retry count exceeded. Disconnecting...");
                disconnect_if_connected();
                retry_exceeded = true;  
            }
            continue;  
        }
/*
        for (int i = 0; i < bytes_written; i += sizeof(uint32_t)) {
        uint32_t sent_value = *(uint32_t *)(data_ptr + i);
        ESP_LOGI(TAG, "Sent uint32_t value: 0x%08X", (unsigned int)sent_value);  // Print in hexadecimal format
    }
*/

        remaining_elements -= bytes_written / sizeof(uint32_t);
        data_ptr += bytes_written;
        total_bytes_sent += bytes_written;
        consecutive_poll_timeout_errors = 0; 
        ESP_LOGI(TAG, "Sent %d bytes over TCP", bytes_written); 
         if(global_digit && (received_integer <= (counter+1))){
            Impulse_Count();   
        }
        counter++;
    }
}
/*
void receive_data_task(void *pvParameters) {
    char recv_buffer[256];
    int recv_len;

    while (1) {
        if (is_connected) {
                recv_len = esp_transport_read(transport, recv_buffer, sizeof(recv_buffer), 250);
                if (recv_len > 0) {
                    //const char *message = "ok"; // Define the message to send
                    //esp_transport_write(transport, message, strlen(message), 250); // Send the message
                    float received_float;
                    memcpy(&received_float, recv_buffer, sizeof(float));
                    received_float = ntohf(received_float); 
                    received_from_server = true;
                    if(!global_digit){
                    ESP_LOGI(TAG, "Received measurement time from server: %.3f s", received_float);
                    uint32_t seconds = (uint32_t)received_float; 
                    float fraction = received_float - seconds;    
                    uint32_t microseconds = fraction * 1000000;  
                        if (!timer_started) {
                            if (timerHandle != NULL) {
                                xTimerDelete(timerHandle, 0);
                            }
                            timerHandle = xTimerCreate("Timer", pdMS_TO_TICKS(seconds * 1000 + microseconds / 1000), pdFALSE, NULL, timerCallback);
                            if (timerHandle != NULL) {
                                xTimerStart(timerHandle, 0);
                                timer_started = true; 
                            }
                        }
                    }
                    else {
                        received_integer = (int)received_float; 
                        ESP_LOGI(TAG, "Received impulse target from server: %d ", received_integer);
                    }
                    vTaskDelay(1000 / portTICK_PERIOD_MS);
                    xSemaphoreGive(dataReceivedSemaphore);
                    vTaskDelete(NULL);//---------------------------------------------------------------------------------
                }
        }
        vTaskDelay(10 / portTICK_PERIOD_MS);  
    }
}
*/

void send_data_task(void *pvParameters) {
    uint32_t received_data[NUM_CYCLES];
    int retry_delay = 1000;  
    
    //Non blocking socket setup--------------------------------------------------------------
    int flags;
    // Get the socket from the transport handle
    int socket_fd = esp_transport_get_errno(transport);
    if (socket_fd < 0) {
        ESP_LOGE(TAG, "Error getting socket from transport");
        vTaskDelete(NULL);
    }

    // Get the current flags for the socket
    if ((flags = fcntl(socket_fd, F_GETFL, 0)) < 0) {
        ESP_LOGE(TAG, "Error getting socket flags");
        vTaskDelete(NULL);
    }
    flags |= O_NONBLOCK;  // Set the non-blocking flag
    // Set the modified flags back to the socket
    if (fcntl(socket_fd, F_SETFL, flags) < 0) {
        ESP_LOGE(TAG, "Error setting socket to non-blocking mode");
        vTaskDelete(NULL);
    }

    //Non blocking socket setup--------------------------------------------------------------
    while (1) {
        if (xSemaphoreTake(tcpMutex, portMAX_DELAY)) {
            if (xQueueReceive(tcpDataQueue, received_data, portMAX_DELAY)) {
                int retries = 0;
                int retry_delay = 0;  

                while (retries < 5) {
                    send_data_over_tcp(received_data, NUM_CYCLES);

                    if (is_connected) {
                        retry_delay = 0;  
                        break;  
                    } else {
                        vTaskDelay(retry_delay / portTICK_PERIOD_MS);
                        retries++;
                        if (retries == 1) {
                            retry_delay = 2000;  
                        } else if (retries == 2) {
                            retry_delay = 3000;  
                        } else if (retries == 3) {
                            retry_delay = 5000;  
                        } else if (retries == 4) {
                            retry_delay = 8000;  
                        }
                    }
                }
                if (retries == 5) {
                    ESP_LOGE(TAG, "Failed to connect or send data in 5 retries. Performing a software reset...");
                    ESP_LOGW(TAG, "Intentional reset triggered by software (esp_restart())");
                    esp_restart();
                }
            } else {
                if (is_connected) {
                    ESP_LOGI(TAG, "Connection lost. Attempting to reconnect...");
                    esp_transport_close(transport);
                    is_connected = false;
                }
            }
            xSemaphoreGive(tcpMutex);
        }
    }
}
static void IRAM_ATTR gpio_interrupt_handler(void *args) {

        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        uint32_t local_data_array[NUM_CYCLES];
        for (int i = 0; i < NUM_CYCLES; ++i) {
            local_data_array[i] = data_array[i];
        }
        xQueueSendFromISR(tcpDataQueue, local_data_array, &xHigherPriorityTaskWoken);
        if (xHigherPriorityTaskWoken == pdTRUE) {
            portYIELD_FROM_ISR();
        }
}
void app_main() {
    vTaskDelay(1000 / portTICK_PERIOD_MS); 

    esp_log_level_set("*", ESP_LOG_INFO);

    esp_log_level_set("transport", ESP_LOG_INFO);
    esp_log_level_set("transport_base", ESP_LOG_INFO); 
    esp_log_level_set("transport_proxy", ESP_LOG_INFO);

    DHCP_timer_handle = xTimerCreate("DHCP_Timer", pdMS_TO_TICKS(60000), pdFALSE, NULL, DHCP_timer_callback);

    uint8_t eth_port_cnt = 0;
    esp_eth_handle_t *eth_handles;
    ESP_ERROR_CHECK(nvs_flash_init());
    vTaskDelay(1000 / portTICK_PERIOD_MS); 
    ESP_ERROR_CHECK(example_eth_init(&eth_handles, &eth_port_cnt));
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

        // if (DHCP_timer_handle == NULL) {
    //   printf("Failed to create DHCP timer\n");
    //}

    if (eth_port_cnt == 1) {
        ESP_LOGI(TAG, "One network adapter found");
        esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
        esp_netif_t *eth_netif = esp_netif_new(&cfg);

        ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0])));
    } else {
        ESP_LOGI(TAG, "Several network adapters found");
        esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
        esp_netif_config_t cfg_spi = {
            .base = &esp_netif_config,
            .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
        };
        char if_key_str[10];
        char if_desc_str[10];
        char num_str[3];
        for (int i = 0; i < eth_port_cnt; i++) {
            itoa(i, num_str, 10);
            strcat(strcpy(if_key_str, "ETH_"), num_str);
            strcat(strcpy(if_desc_str, "eth"), num_str);
            esp_netif_config.if_key = if_key_str;
            esp_netif_config.if_desc = if_desc_str;
            esp_netif_config.route_prio -= i*5;
            esp_netif_t *eth_netif = esp_netif_new(&cfg_spi);

            ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i])));
        }
    }
    ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));

    for (int i = 0; i < eth_port_cnt; i++) {
        ESP_ERROR_CHECK(esp_eth_start(eth_handles[i]));
    }

    while(!protocol_transition){
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }

    transport = esp_transport_tcp_init();
    establish_connection();
/*
    esp_rom_gpio_pad_select_gpio(INTERRUPT_INPUT);
    gpio_set_direction(INTERRUPT_INPUT, GPIO_MODE_INPUT);
    gpio_pulldown_en(INTERRUPT_INPUT);
    gpio_pullup_dis(INTERRUPT_INPUT);
    gpio_set_intr_type(INTERRUPT_INPUT, GPIO_INTR_NEGEDGE);

    gpio_config_t io_conf_output = {
            .pin_bit_mask = (1ULL << GPIO_CLOCK),
            .mode = GPIO_MODE_OUTPUT,
        };
        gpio_config(&io_conf_output);

        gpio_config_t io_conf;
        for (int i = 0; i < NUM_GPIO_PINS; ++i) {
            io_conf = (gpio_config_t){
                .pin_bit_mask = (1ULL << GPIO_PINS[i]),
                .mode = GPIO_MODE_INPUT,
                .intr_type = GPIO_INTR_DISABLE,
                .pull_up_en = GPIO_PULLUP_DISABLE,
                .pull_down_en = GPIO_PULLDOWN_ENABLE,
            };
            gpio_config(&io_conf);
        }
    */
    //dataReceivedSemaphore = xSemaphoreCreateBinary();
    
    //xTaskCreatePinnedToCore(receive_data_task, "TCP_Task", 4096, NULL, 7, &tcpTaskHandle, APP_CPU_NUM);  // Increased priority
    /*
    if (xSemaphoreTake(dataReceivedSemaphore, portMAX_DELAY) == pdTRUE) {
        tcpDataQueue = xQueueCreate(1, sizeof(data_array));

        xTaskCreatePinnedToCore(send_data_task, "TCP_Task", 8192, NULL, 4, &tcpTaskHandle, APP_CPU_NUM);  // Increased priority

        tcpMutex = xSemaphoreCreateMutex();

    gpio_install_isr_service(0);
    gpio_isr_handler_add(INTERRUPT_INPUT, gpio_interrupt_handler, (void *)INTERRUPT_INPUT);

    }
    */
   /*
    uint8_t k = 0;
    while (1) {
        REG_WRITE(GPIO_OUT_W1TS_REG, (1 << GPIO_CLOCK));
        data_array[k] =REG_READ(GPIO_IN_REG);
        REG_WRITE(GPIO_OUT_W1TC_REG, (1 << GPIO_CLOCK));
        k = (k + 1) % NUM_CYCLES;
    }
    */
//--------------------------------------------------------------------------------------
 char recv_buffer[256];
    int recv_len;
      

    while (1) {
        if (is_connected) {
            recv_len = esp_transport_read(transport, recv_buffer, sizeof(recv_buffer), 250);
            if (recv_len > 0) {
                float received_float;
                memcpy(&received_float, recv_buffer, sizeof(float));
                received_float = ntohf(received_float); 
                received_from_server = true;

                
                const char *ack_message = "ACK";
                esp_transport_write(transport, ack_message, strlen(ack_message), 250);
                vTaskDelay(100 / portTICK_PERIOD_MS);
                esp_transport_write(transport, ack_message, strlen(ack_message), 250);
                vTaskDelay(100 / portTICK_PERIOD_MS);
                esp_transport_write(transport, ack_message, strlen(ack_message), 250);
                vTaskDelay(100 / portTICK_PERIOD_MS);
                esp_transport_write(transport, ack_message, strlen(ack_message), 250);
                vTaskDelay(100 / portTICK_PERIOD_MS);
                
                if (!global_digit) {
                    ESP_LOGI(TAG, "Received measurement time from server: %.3f s", received_float);
                    uint32_t seconds = (uint32_t)received_float; 
                    float fraction = received_float - seconds;    
                    uint32_t microseconds = fraction * 1000000;  
                    if (!timer_started) {
                        if (timerHandle != NULL) {
                            xTimerDelete(timerHandle, 0);
                        }
                        timerHandle = xTimerCreate("Timer", pdMS_TO_TICKS(seconds * 1000 + microseconds / 1000), pdFALSE, NULL, timerCallback);
                        if (timerHandle != NULL) {
                            xTimerStart(timerHandle, 0);
                            timer_started = true; 
                        }
                    }
                    
                } else {
                    received_integer = (int)received_float; 
                    ESP_LOGI(TAG, "Received impulse target from server: %d ", received_integer);
                }
                //break;
                /*
                // Sending ACK back to the server
                const char *message = "ok"; // Define the message to send
                int write_result = esp_transport_write(transport, message, strlen(message), 250);
                if (write_result < 0) {
                    ESP_LOGE(TAG, "Failed to send ACK: errno %d", errno);
                } else {
                    ESP_LOGI(TAG, "ACK sent successfully.");
                }
                */
            vTaskDelay(5000 / portTICK_PERIOD_MS);
            close_connection(); 
            }
             vTaskDelay(10 / portTICK_PERIOD_MS);
        }
        //vTaskDelay(500 / portTICK_PERIOD_MS);
    }

//--------------------------------------------------------------------------------------

    //close_connection();

#ifdef CONFIG_EXAMPLE_ENABLE_PROXY
    esp_transport_destroy(parent);
#endif
}
