#include "epd_s3.h"
#include <stdio.h>
#include "esp_log.h"
#include "xag_nvs_component.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_littlefs.h"
#include "xag_wifi_component.h"
#include "esp_http_client.h"
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_sleep.h"
#include <esp_system.h>
#include "driver/rtc_io.h"
#include "Config.h"
#include "MultiButtonHeader.h"


#define MAX_PATH_LENGTH 255
#define HTTP_RECEIVE_BUFFER_SIZE 4096
#define MAX_FILES 100
#define MAX_FILES 100
#define MAX_RETRIES 3

extern void set_led_blink_multi(bool red1, bool green1, bool blue1, 
                         bool red2, bool green2, bool blue2, 
                         int blink_interval_ms);
typedef struct {
    char filename[MAX_PATH_LENGTH];
} FileInfo;
FileInfo files_to_download[MAX_FILES];
int file_count = 0;
static const char *TAG = "HTTP_CLIENT";
char black_file_path[MAX_PATH_LENGTH] = {0};
char red_file_path[MAX_PATH_LENGTH] = {0};
/////////////////////////////Checkout
esp_err_t disconnect_wifi() {
    esp_err_t err = esp_wifi_disconnect();
    if (err == ESP_OK) {
        ESP_LOGI("WIFI", "WiFi disconnected successfully");
    } else {
        ESP_LOGE("WIFI", "Failed to disconnect WiFi: %s", esp_err_to_name(err));
    }
    return err;
}
esp_err_t stop_wifi() {
    esp_err_t err = esp_wifi_stop();
    if (err == ESP_OK) {
        ESP_LOGI("WIFI", "WiFi stopped successfully");
    } else {
        ESP_LOGE("WIFI", "Failed to stop WiFi: %s", esp_err_to_name(err));
    }
    
    err = esp_wifi_deinit();
    if (err == ESP_OK) {
        ESP_LOGI("WIFI", "WiFi deinitialized successfully");
    } else {
        ESP_LOGE("WIFI", "Failed to deinitialize WiFi: %s", esp_err_to_name(err));
    }
    
    return err;
}
////////////////////////////////

// Updated function to get, print, and categorize full paths of all files in LittleFS
void print_and_categorize_file_paths()
{
    ESP_LOGI(TAG, "Printing and categorizing full paths of all files in LittleFS:");
    DIR *dir = opendir("/littlefs");
    if (dir == NULL) {
        ESP_LOGE(TAG, "Failed to open LittleFS directory");
        return;
    }

    struct dirent *entry;
    char filepath[MAX_PATH_LENGTH];
    while ((entry = readdir(dir)) != NULL) {
        if (snprintf(filepath, sizeof(filepath), "/littlefs/%s", entry->d_name) >= sizeof(filepath)) {
            ESP_LOGE(TAG, "Filepath too long for: %s", entry->d_name);
            continue;
        }
        struct stat st;
        if (stat(filepath, &st) == 0) {
            if (S_ISREG(st.st_mode)) {  // Check if it's a regular file
                ESP_LOGI(TAG, "File: %s", filepath);
                
                // Categorize the file based on its name
                if (strstr(entry->d_name, "B") != NULL) {
                    strncpy(black_file_path, filepath, sizeof(black_file_path) - 1);
                    black_file_path[sizeof(black_file_path) - 1] = '\0';  // Ensure null-termination
                    ESP_LOGI(TAG, "Categorized as Black: %s", black_file_path);
                } else if (strstr(entry->d_name, "R") != NULL) {
                    strncpy(red_file_path, filepath, sizeof(red_file_path) - 1);
                    red_file_path[sizeof(red_file_path) - 1] = '\0';  // Ensure null-termination
                    ESP_LOGI(TAG, "Categorized as Red: %s", red_file_path);
                }
            } else if (S_ISDIR(st.st_mode)) {  // Check if it's a directory
                ESP_LOGI(TAG, "Directory: %s", filepath);
            }
        } else {
            ESP_LOGE(TAG, "Failed to get file info for: %s", filepath);
        }
    }
    closedir(dir);
}

// New function to read the first 100 characters of all downloaded files
void read_first_100_chars()
{
    ESP_LOGI(TAG, "Reading first 100 characters of downloaded files:");
    for (int i = 0; i < file_count; i++) {
        char filepath[MAX_PATH_LENGTH];
        if (snprintf(filepath, sizeof(filepath), "/littlefs/%s", files_to_download[i].filename) >= sizeof(filepath)) {
            ESP_LOGE(TAG, "Filepath too long for: %s", files_to_download[i].filename);
            continue;
        }
        
        FILE* file = fopen(filepath, "r");
        if (file == NULL) {
            ESP_LOGE(TAG, "Failed to open file: %s", filepath);
            continue;
        }
        
        char buffer[101];
        size_t bytes_read = fread(buffer, 1, 100, file);
        fclose(file);
        
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            ESP_LOGI(TAG, "File: %s\nContent: %s", files_to_download[i].filename, buffer);
        } else {
            ESP_LOGE(TAG, "Failed to read file: %s", filepath);
        }
    }
}

// New function to delete all files in LittleFS
void delete_all_files()
{
    ESP_LOGI(TAG, "Deleting all files in LittleFS");
    DIR *dir = opendir("/littlefs");
    if (dir == NULL) {
        ESP_LOGE(TAG, "Failed to open LittleFS directory");
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        char filepath[MAX_PATH_LENGTH];
        if (snprintf(filepath, sizeof(filepath), "/littlefs/%s", entry->d_name) >= sizeof(filepath)) {
            ESP_LOGE(TAG, "Filepath too long for: %s", entry->d_name);
            continue;
        }
        if (unlink(filepath) != 0) {
            ESP_LOGE(TAG, "Failed to delete file: %s", filepath);
        } else {
            ESP_LOGI(TAG, "Deleted file: %s", filepath);
        }
    }
    closedir(dir);
}

// New function to list all files in LittleFS
void list_all_files()
{
    ESP_LOGI(TAG, "Listing all files in LittleFS:");
    DIR *dir = opendir("/littlefs");
    if (dir == NULL) {
        ESP_LOGE(TAG, "Failed to open LittleFS directory");
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        char filepath[MAX_PATH_LENGTH];
        if (snprintf(filepath, sizeof(filepath), "/littlefs/%s", entry->d_name) >= sizeof(filepath)) {
            ESP_LOGE(TAG, "Filepath too long for: %s", entry->d_name);
            continue;
        }
        struct stat st;
        if (stat(filepath, &st) == 0) {
            ESP_LOGI(TAG, "File: %s, Size: %ld bytes", entry->d_name, st.st_size);
        }
    }
    closedir(dir);
}
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    static FILE *file = NULL;
    static uint32_t total_bytes = 0;
    static struct timeval start_time, end_time;

    switch (evt->event_id) {
        case HTTP_EVENT_ON_CONNECTED:
            gettimeofday(&start_time, NULL);
            file = fopen(evt->user_data, "wb");
            if (file == NULL) {
                ESP_LOGE(TAG, "Failed to open file for writing: %s", (char*)evt->user_data);
                return ESP_FAIL;
            }
            total_bytes = 0;
            break;
        case HTTP_EVENT_ON_DATA:
            if (file != NULL) {
                fwrite(evt->data, 1, evt->data_len, file);
                total_bytes += evt->data_len;
            }
            break;
        case HTTP_EVENT_ON_FINISH:
            if (file != NULL) {
                fclose(file);
                file = NULL;
                gettimeofday(&end_time, NULL);
                double time_taken = (end_time.tv_sec - start_time.tv_sec) * 1000.0;
                time_taken += (end_time.tv_usec - start_time.tv_usec) / 1000.0;
                ESP_LOGI(TAG, "File download complete: %s, %lu bytes in %.2f ms", 
                         (char*)evt->user_data, (unsigned long)total_bytes, time_taken);
            }
            break;
        case HTTP_EVENT_DISCONNECTED:
            if (file != NULL) {
                fclose(file);
                file = NULL;
            }
            break;
        default:
            break;
    }
    return ESP_OK;
}
// Improved definitions
#define MIN_CHUNK_SIZE 4096   // 4 KB minimum chunk size
#define MAX_CHUNK_SIZE 16384  // 16 KB maximum chunk size

static esp_err_t improved_chunk_download_file(const char* url, const char* filename)
{
    char filepath[MAX_PATH_LENGTH];
    snprintf(filepath, sizeof(filepath), "/littlefs/%s", filename);

    esp_http_client_config_t config = {
        .url = url,
        .timeout_ms = 15000, // 15 second timeout
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);
    if (client == NULL) {
        ESP_LOGE(TAG, "Failed to initialize HTTP client");
        return ESP_FAIL;
    }

    FILE* file = fopen(filepath, "wb");
    if (file == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing: %s", filepath);
        esp_http_client_cleanup(client);
        return ESP_FAIL;
    }

    esp_err_t err;
    size_t chunk_size = MAX_CHUNK_SIZE;
    char* buffer = NULL;

    // Try to allocate buffer, reduce size if necessary
    while (chunk_size >= MIN_CHUNK_SIZE) {
        buffer = heap_caps_malloc(chunk_size, MALLOC_CAP_8BIT);
        if (buffer != NULL) {
            break;
        }
        chunk_size /= 2;
    }

    if (buffer == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for download buffer");
        fclose(file);
        esp_http_client_cleanup(client);
        return ESP_ERR_NO_MEM;
    }

    ESP_LOGI(TAG, "Using chunk size: %d bytes", chunk_size);

    int64_t total_bytes = 0;
    int read_len;

    err = esp_http_client_open(client, 0);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
        goto cleanup;
    }

    int content_length = esp_http_client_fetch_headers(client);
    ESP_LOGI(TAG, "Content Length: %d", content_length);

    do {
        read_len = esp_http_client_read(client, buffer, chunk_size);
        if (read_len > 0) {
            set_led_blink_alternate(false, true, true, false, false, false, 200);
            size_t written = fwrite(buffer, 1, read_len, file);
            if (written != read_len) {
                ESP_LOGE(TAG, "Failed to write data to file");
                err = ESP_FAIL;
                goto cleanup;
            }
            total_bytes += read_len;
            ESP_LOGI(TAG, "Downloaded %lld bytes", total_bytes);
        }
    } while (read_len > 0);

    if (esp_http_client_is_complete_data_received(client) != true) {
        ESP_LOGE(TAG, "Error in receiving complete file");
        err = ESP_FAIL;
        goto cleanup;
    }

    if (read_len == 0) {
        ESP_LOGI(TAG, "File downloaded successfully: %s, Total: %lld bytes", filename, total_bytes);
        err = ESP_OK;
    } else if (read_len < 0) {
        ESP_LOGE(TAG, "Error in downloading file: %s, %s", filename, esp_err_to_name(read_len));
        err = ESP_FAIL;
    }

cleanup:
    free(buffer);
    fclose(file);
    esp_http_client_cleanup(client);
    return err;
}


static esp_err_t list_remote_files(const char* base_url)
{
    set_led_blink_alternate(false, true, true, false, false, false, 300);
    esp_http_client_config_t config = {
        .url = base_url,
        .method = HTTP_METHOD_GET,
        .timeout_ms = 10000,  // 10 second timeout
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);
    if (client == NULL) {
        ESP_LOGE(TAG, "Failed to initialize HTTP client");
        return ESP_FAIL;
    }

    esp_err_t err;
    int retries = 0;
    while (retries < MAX_RETRIES) {
        err = esp_http_client_open(client, 0);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
            retries++;
            vTaskDelay(1000 / portTICK_PERIOD_MS);  // Wait 1 second before retry
            continue;
        }

        int content_length = esp_http_client_fetch_headers(client);
        if (content_length < 0) {
            ESP_LOGE(TAG, "HTTP client fetch headers failed");
            retries++;
            esp_http_client_close(client);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }

        ESP_LOGI(TAG, "Content length: %d", content_length);

        char *buffer = malloc(content_length + 1);
        if (buffer == NULL) {
            ESP_LOGE(TAG, "Failed to allocate memory for response buffer");
            esp_http_client_close(client);
            esp_http_client_cleanup(client);
            return ESP_ERR_NO_MEM;
        }

        int read_len = 0;
        int total_read = 0;
        while (total_read < content_length) {
            read_len = esp_http_client_read(client, buffer + total_read, content_length - total_read);
            if (read_len <= 0) {
                ESP_LOGE(TAG, "Error reading data");
                free(buffer);
                esp_http_client_close(client);
                retries++;
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                break;
            }
            total_read += read_len;
            ESP_LOGI(TAG, "Read %d bytes, total %d", read_len, total_read);
        }

        if (total_read == content_length) {
            buffer[total_read] = '\0';

            // Parse the HTML to extract .h file names
            char *line = strtok(buffer, "\n");
            while (line != NULL && file_count < MAX_FILES) {
                if (strstr(line, "href=\"") && strstr(line, ".h\"")) {
                    char *start = strstr(line, "href=\"") + 6;
                    char *end = strstr(start, "\"");
                    if (end != NULL) {
                        int len = end - start;
                        if (len < MAX_PATH_LENGTH) {
                            strncpy(files_to_download[file_count].filename, start, len);
                            files_to_download[file_count].filename[len] = '\0';
                            ESP_LOGI(TAG, "Found .h file: %s", files_to_download[file_count].filename);
                            file_count++;
                        }
                    }
                }
                line = strtok(NULL, "\n");
            }

            free(buffer);
            esp_http_client_close(client);
            esp_http_client_cleanup(client);
            return ESP_OK;
        }
    }

    ESP_LOGE(TAG, "Failed to read data after %d retries", MAX_RETRIES);
    esp_http_client_close(client);
    esp_http_client_cleanup(client);
    return ESP_FAIL;
}
void enter_deep_sleep_mode(void) {
    ESP_LOGI(TAG, "Configuring wakeup for deep sleep mode");

    // Configure EXT1 wake-up source
    esp_sleep_enable_ext1_wakeup((1ULL << CONFIG_BUTTON_USER_GPIO), ESP_EXT1_WAKEUP_ANY_LOW);

    // Configure RTC GPIO
    rtc_gpio_init(CONFIG_BUTTON_USER_GPIO);
    rtc_gpio_set_direction(CONFIG_BUTTON_USER_GPIO, RTC_GPIO_MODE_INPUT_ONLY);
    rtc_gpio_pullup_en(CONFIG_BUTTON_USER_GPIO);

    ESP_LOGI(TAG, "Entering deep sleep mode");

    // Enter deep sleep
    esp_deep_sleep_start();
    // The device will continue execution here after waking up from deep sleep
}

void app_main(void)
{
	    esp_err_t err;

    // Initialize NVS
    err = init_nvs();
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error initializing NVS: %s", esp_err_to_name(err));
        return;
    }
    init_led_control();
    
   // Initialize LittleFS
    esp_vfs_littlefs_conf_t conf = {
        .base_path = "/littlefs",
        .partition_label = "storage",
        .format_if_mount_failed = true,
    };

    esp_err_t ret = esp_vfs_littlefs_register(&conf);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to mount LittleFS (%s)", esp_err_to_name(ret));
        return;
    }
    set_led_blink_single(false,false,true,200);
 	 StoreHardcodeWiFiData("Airtel_Xaglabs","GMArc@de#58");
    // Connect to Wi-Fi
if (connectWifi() == ESP_OK) {
        ESP_LOGI(TAG, "Connected to Wi-Fi successfully.");
        delete_all_files();
        const char *base_url = "http://trials.nudgeenergy.co.uk/ftpuser/NudgeDisplay/devices/9/";
        char *filename[] = {"redR.h", "blackB.h"};

        // List files before download
        ESP_LOGI(TAG, "Files in LittleFS before download:");
        list_all_files();
        // List remote files
        if (list_remote_files(base_url) == ESP_OK) {
            ESP_LOGI(TAG, "Found %d .h files to download", file_count);
            printf("Full URL: %s\n",base_url);
            // Download each .h file
            for (int i = 0; i < 2; i++) {
                char full_url[MAX_PATH_LENGTH * 2];
                snprintf(full_url, sizeof(full_url), "%s%s", base_url, filename[i]);
                ESP_LOGI(TAG,"Full URL: %s\n",full_url);
                ESP_LOGI(TAG, "Downloading file: %s", filename[i]);
                if (improved_chunk_download_file(full_url, filename[i]) == ESP_OK) {
                    ESP_LOGI(TAG, "File downloaded successfully: %s", filename[i]);
                    stop_leds();
                } else {
                    set_led_blink_alternate(false, true, true, true, false, false, 300);
                    vTaskDelay(500);
                    enter_deep_sleep_mode();
                    ESP_LOGE(TAG, "Failed to download file: %s", filename[i]);
                }
            }

            // List files after download
            ESP_LOGI(TAG, "Files in LittleFS after download:");
            list_all_files();


            // Print and categorize all file paths after download
            ESP_LOGI(TAG, "Files in LittleFS after download:");
            print_and_categorize_file_paths();

            // Read first 100 characters of downloaded files
            read_first_100_chars();

            // Print the categorized file paths
            ESP_LOGI(TAG, "Categorized file paths:");
            ESP_LOGI(TAG, "Black file: %s", black_file_path);
            ESP_LOGI(TAG, "Red file: %s", red_file_path);
             //  If you want to completely stop WiFi
  //  stop_wifi();
    stop_leds();
	epd_s3_main();
    set_led_blink_single(false,false,true,200);
    SetupMultiButton();
    stop_leds();
    vTaskDelay(1000);
		}
        else {
        printf("This is happening");
        set_led_blink_single(true,false,false,200);
        vTaskDelay(2000);
        enter_deep_sleep_mode();
        }
    }else {
        printf("This wifi is happening");
       // set_led_blink_multi(true,false,false,false,false,true,200);
        vTaskDelay(2000);
        enter_deep_sleep_mode();
        }
set_led_blink_single(true,false,false,200);
vTaskDelay(3000);
enter_deep_sleep_mode();
}
