#include "epaper_manager.h"
#include "wifi_manager.h"
#include "tcp_debug.h"
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "pico/flash.h"
#include "pico/util/queue.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// e-Paper library includes
extern "C" {
    #include "DEV_Config.h"
    #include "EPD_4in2_V2.h"
    #include "GUI_Paint.h"
}

// e-Paper display buffers
static UBYTE *g_epd_image = NULL;  // Black image buffer

// Entry list for display
#define MAX_ENTRIES 10
#define ENTRY_LENGTH 64
typedef struct {
    char entries[MAX_ENTRIES][ENTRY_LENGTH];
    int count;
} EntryList;

static EntryList g_entry_list = {{}, 0};
static bool g_force_full_refresh = false;  // Flag to force full refresh when list is full

// Synchronization flag for multicore initialization
static volatile bool g_display_ready = false;

// Queue for display messages (replacing raw FIFO to avoid conflict with flash_safe_execute)
static queue_t g_display_queue;
#define QUEUE_LENGTH 8

// Display update message structure for inter-core communication
typedef struct {
    EntryList entries;
    bool use_partial;  // Use partial refresh instead of full refresh
    UWORD xstart;      // Partial refresh region
    UWORD ystart;
    UWORD xend;
    UWORD yend;
    UBYTE *raw_image;  // Pointer to raw image data (if not NULL, display this instead of text)
} DisplayMessage;

static void init_epaper_display() {
    printf("[Core 1] Initializing 4.2\" e-Paper display (V2)...\r\n");
    
    // Initialize the hardware
    if (DEV_Module_Init() != 0) {
        printf("[Core 1] Failed to initialize e-Paper hardware!\r\n");
        return;
    }
    
    // Initialize the display
    printf("[Core 1] EPD_4IN2_V2_Init_Fast()\r\n");
    EPD_4IN2_V2_Init();
    printf("[Core 1] EPD_4IN2_V2_Clear()\r\n");
    EPD_4IN2_V2_Clear();
    
    // Create image buffers for black content
    // 4.2" display: 400x300, 8 pixels per byte
    UWORD imagesize = ((EPD_4IN2_V2_WIDTH % 8 == 0)? (EPD_4IN2_V2_WIDTH / 8 ): (EPD_4IN2_V2_WIDTH / 8 + 1)) * EPD_4IN2_V2_HEIGHT;
    
    g_epd_image = (UBYTE *)malloc(imagesize);
    
    if (g_epd_image == NULL) {
        printf("[Core 1] Failed to allocate memory for e-Paper image buffers!\r\n");
        return;
    }
    
    // Setup paint buffer for black content
    printf("[Core 1] White background\r\n");
    Paint_NewImage(g_epd_image, EPD_4IN2_V2_WIDTH, EPD_4IN2_V2_HEIGHT, 0, WHITE);
    Paint_SelectImage(g_epd_image);
    Paint_Clear(WHITE);

    printf("[Core 1] Trying to connect to wifi\r\n");
    bool connected = wifi_try_auto_connect();
    printf("[Core 1] Connected: %d\r\n", connected);
    
    if (connected) {
        tcp_debug_init();
    }

    Paint_SelectImage(g_epd_image);
    printf("[Core 1] Drawing header\r\n");
    if (connected) {
        char header_text[64];
        const char* ip = wifi_get_ip();
        snprintf(header_text, sizeof(header_text), "IP: %s", ip ? ip : "Unknown");
        Paint_DrawString_EN(10, 10, header_text, &Font24, WHITE, BLACK);
    } else {
        Paint_DrawString_EN(10, 10, "What's new today:", &Font24, WHITE, BLACK);
    }
    
    
    // Display the image
    EPD_4IN2_V2_PartialDisplay(g_epd_image, 0, 0, EPD_4IN2_V2_WIDTH, 40);
    // EPD_4IN2_V2_Display_Fast(g_epd_image);
    
    printf("[Core 1] e-Paper display ready!\r\n");
}

/**
 * Core 1 function: Runs display initialization and update handling
 */
static void core1_display_init() {
    // Initialize flash safe execute support for this core
    flash_safe_execute_core_init();

    // Initialize WiFi on Core 1 so IRQs are handled here
    wifi_init();

    init_epaper_display();
    g_display_ready = true;
    
    printf("[Core 1] Display ready, waiting for update messages...\n");
    
    // Initialize for partial refresh on second call
    // EPD_4IN2_V2_Init_Fast(Seconds_1S); // Already done in init
    
    // Core 1 main loop: handle display updates via Queue
    while (true) {
        DisplayMessage *msg;
        // Check if there's a message in the queue
        if (queue_try_remove(&g_display_queue, &msg)) {
            
            // Drain Queue to get the latest message
            DisplayMessage *next_msg;
            while (queue_try_remove(&g_display_queue, &next_msg)) {
                 // printf("[Core 1] Skipping intermediate update\n");
                 free(msg); // Free the stale message
                 msg = next_msg;
            }
            
            // printf("[Core 1] Updating display with %d entries\n", msg->entries.count);
            
            // Update the e-Paper display
            if (msg->raw_image != NULL) {
                printf("[Core 1] Displaying raw image...\n");
                EPD_4IN2_V2_Display(msg->raw_image);
                free(msg->raw_image); // Free the image buffer allocated by Core 0
            } else if (g_epd_image != NULL) {
                // For partial refresh, only clear the text area
                UWORD y_start = msg->ystart;
                UWORD y_end = msg->yend;
                
                Paint_SelectImage(g_epd_image);
                Paint_Clear(WHITE);
                
                // Calculate offset for partial refresh
                // UBYTE *image_ptr = g_epd_image + (msg->ystart * (EPD_4IN2_V2_WIDTH / 8));

                if(!msg->use_partial){
                    // for loop 5 times display white partial
                    Paint_Clear(WHITE);
                    int i = 0;
                    for(i = 0; i < 1; i++){
                        EPD_4IN2_V2_PartialDisplay(g_epd_image, 0, 0, 400, 300);
                    }
                }
                
                // Draw all entries starting below header
                UWORD y_pos = 0;
                // paint only the last 10 entries
                int start_index = msg->entries.count > 10 ? msg->entries.count - 10 : 0;
                for (int i = start_index; i < msg->entries.count; i++) {
                    if (y_pos + 25 < 300) {  //Don't draw beyond screen
                        Paint_DrawString_EN(20, 50 + i*25, msg->entries.entries[i], &Font16, WHITE, BLACK);
                        y_pos += 25;  // Space between entries
                    } else {
                        printf("[Core 1] Skipping entry to avoid overflow\n");
                    }
                }
                // Use partial or full refresh
                // printf("[Core 1] Using partial refresh\n");
                EPD_4IN2_V2_PartialDisplay(g_epd_image, 0, 0, 400, 300);
            }
            
            // Free the message (it was allocated by core 0)
            free(msg);
        }
        
        sleep_ms(10);
    }
}

void epaper_start_background_thread() {
    // Initialize the queue before starting the thread
    queue_init(&g_display_queue, sizeof(DisplayMessage*), QUEUE_LENGTH);

    printf("[Core 0] Launching e-Paper display init on core 1...\n");
    multicore_launch_core1(core1_display_init);
}

void epaper_send_update(const char *entry, bool finish_line) {
    if (!g_display_ready) return;  // Don't send if display isn't ready
    // printf("[Core 0] Preparing display update: '%s' (finish_line=%d)\n", entry, finish_line);
    
    // Check if list is at capacity
    if (g_entry_list.count >= MAX_ENTRIES) {
        // List is full - clear all entries and start fresh
        printf("[Core 0] List is FULL - clearing all entries and starting fresh\n");
        g_entry_list.count = 0;
        g_force_full_refresh = true;
    }
    
    // Update the current entry (at g_entry_list.count)
    strncpy(g_entry_list.entries[g_entry_list.count], entry, ENTRY_LENGTH - 1);
    g_entry_list.entries[g_entry_list.count][ENTRY_LENGTH - 1] = '\0';
    
    // Allocate message structure
    DisplayMessage *msg = (DisplayMessage *)malloc(sizeof(DisplayMessage));
    if (msg == NULL) {
        printf("[Core 0] Failed to allocate display message!\n");
        return;
    }
    
    // Copy entry list to message
    msg->entries = g_entry_list;
    msg->raw_image = NULL; // Not an image update
    // We want to show the current line being edited, so we treat count as count + 1 for display purposes
    msg->entries.count = g_entry_list.count + 1;
    
    // Decide whether to use partial or full refresh
    msg->use_partial = !g_force_full_refresh;
    
    if (g_force_full_refresh) {
        printf("[Core 0] Forcing full refresh this update\n");
        g_force_full_refresh = false;  // Reset flag after setting it in message
        
        // Full text area refresh
        msg->xstart = 0;
        msg->ystart = 50;      // Start below the header
        msg->xend = EPD_4IN2_V2_WIDTH;       // Full width
        msg->yend = EPD_4IN2_V2_HEIGHT;       // Full height from header onwards
    } else {
        // Partial refresh of ONLY the current line
        msg->xstart = 0;
        msg->ystart = 50 + (g_entry_list.count * 25);
        msg->xend = EPD_4IN2_V2_WIDTH;
        msg->yend = msg->ystart + 25;
    }
    
    // Send message pointer to core 1 via Queue
    if (queue_try_add(&g_display_queue, &msg)) {
        // printf("[Core 0] Display update sent to core 1\n");
    } else {
        static int full_count = 0;
        if (full_count < 5) {
             printf("[Core 0] Queue full, skipping display update\n");
             full_count++;
        }
        free(msg);
    }
    
    // If finishing the line, increment count for next time
    if (finish_line) {
        g_entry_list.count++;
    }
}

void epaper_clear() {
    g_entry_list.count = 0;
    memset(g_entry_list.entries, 0, sizeof(g_entry_list.entries));
    g_force_full_refresh = true;
    epaper_send_update("", false);
}

void epaper_force_refresh() {
    g_force_full_refresh = true;
    epaper_send_update("", false);
}

void epaper_display_full_image(const unsigned char* image_data, unsigned int len) {
    if (!g_display_ready) return;

    // Allocate message structure
    DisplayMessage *msg = (DisplayMessage *)malloc(sizeof(DisplayMessage));
    if (msg == NULL) {
        printf("[Core 0] Failed to allocate display message for image!\n");
        return;
    }

    // We are passing ownership of the image buffer to the message/Core 1
    // The caller should have allocated image_data with malloc
    msg->raw_image = (UBYTE*)image_data;
    msg->use_partial = false; // Full refresh for images

    // Send message pointer to core 1 via Queue
    if (queue_try_add(&g_display_queue, &msg)) {
        printf("[Core 0] Image update sent to core 1\n");
    } else {
        printf("[Core 0] Queue full, skipping image update\n");
        free(msg);
        // Note: We don't free image_data here because the caller might expect it to be consumed or not.
        // Ideally, if we fail to send, we should free it if we took ownership.
        // Let's assume we take ownership.
        free((void*)image_data);
    }
}
