#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "pico/util/queue.h"
#include "hardware/timer.h"

// e-Paper library includes
#include "DEV_Config.h"
#include "GUI_Paint.h"
#include "fonts.h"
#include "EPD_4in2_V2.h"

//Create a new image cache
UBYTE *BlackImage;

queue_t q;

void process_command(char *cmd);
void core1_entry();

bool screen_on = false;
uint32_t screen_last_ts = 0;

int epaper_init()
{
    printf("EPD_4IN2_V2_test Demo\r\n");
    if(DEV_Module_Init()!=0){
        return -1;
    }

    printf("e-Paper Init and Clear...\r\n");
    EPD_4IN2_V2_Init();
    EPD_4IN2_V2_Clear();
    // DEV_Delay_ms(500);

    /* you have to edit the startup_stm32fxxx.s file and set a big enough heap size */
    UWORD Imagesize = ((EPD_4IN2_V2_WIDTH % 8 == 0)? (EPD_4IN2_V2_WIDTH / 8 ): (EPD_4IN2_V2_WIDTH / 8 + 1)) * EPD_4IN2_V2_HEIGHT;
    if((BlackImage = (UBYTE *)malloc(Imagesize)) == NULL) {
        printf("Failed to apply for black memory...\r\n");
        return -1;
    }
    printf("Paint_NewImage\r\n");
    Paint_NewImage(BlackImage, EPD_4IN2_V2_WIDTH, EPD_4IN2_V2_HEIGHT, 0, WHITE);
    screen_on = true;
    return 1;
}

bool editor_mode = false;
uint32_t editor_last_ts = 0;
char screen_log[2000];
int screen_log_i = 0;
sFONT *current_font = &Font16;

int epaper_close()
{
    DEV_Delay_ms(1000);
    //EPD_4IN2_V2_Init();
    //EPD_4IN2_V2_Clear();
    EPD_4IN2_V2_Display(BlackImage);
    DEV_Delay_ms(1000);
    printf("Goto Sleep...\r\n");
    EPD_4IN2_V2_Sleep();
    free(BlackImage);
    BlackImage = NULL;
    DEV_Delay_ms(2000);//important, at least 2s
    screen_on = false;
    return 0;
}

void process_command(char *cmd)
{
    printf("Processing command: %s\n", cmd);
    if (strcmp(cmd, "init") == 0) {
        epaper_init();
        printf("e-Paper initialized\n");
    } else if (screen_on && strcmp(cmd, "clear") == 0) {
        Paint_Clear(WHITE);
        printf("e-Paper cleared\n");
    } else if (screen_on && strcmp(cmd, "display") == 0) {
        EPD_4IN2_V2_Display(BlackImage);
        printf("Display updated\n");
    } else if (screen_on && strcmp(cmd, "draw_test") == 0) {
        Paint_Clear(WHITE);
        Paint_DrawString_EN(10, 0, "www.waveshare.com", &Font24, WHITE, BLACK);
        Paint_DrawString_EN(10, 35, "Pico-ePaper-4.2", &Font24, BLACK, WHITE);
        Paint_DrawString_CN(10, 70, "你好microPython", &Font24CN, WHITE, BLACK);
        Paint_DrawString_CN(10, 110, "微雪电子", &Font24CN, BLACK, WHITE);
        EPD_4IN2_V2_Display(BlackImage);
        printf("Draw test complete\n");
    } else if (screen_on && strncmp(cmd, "draw_text", 9) == 0) {
        int x, y;
        char text[200];
        if (sscanf(cmd, "draw_text %d %d %199[^\n]", &x, &y, text) == 3) {
            Paint_DrawString_EN(x, y, text, current_font, WHITE, BLACK);
            printf("Drew text at %d, %d: %s\n", x, y, text);
        } else {
            printf("Invalid draw_text command format. Use: draw_text x y text\n");
        }
    } else if (screen_on && strncmp(cmd, "draw_point", 10) == 0) {
        int x, y;
        if (sscanf(cmd, "draw_point %d %d", &x, &y) == 2) {
            Paint_DrawPoint(x, y, BLACK, DOT_PIXEL_1X1, DOT_STYLE_DFT);
            printf("Drew point at %d, %d\n", x, y);
        } else {
            printf("Invalid draw_point command format. Use: draw_point x y\n");
        }
    } else if (screen_on && strncmp(cmd, "draw_line", 9) == 0) {
        int x1, y1, x2, y2;
        if (sscanf(cmd, "draw_line %d %d %d %d", &x1, &y1, &x2, &y2) == 4) {
            Paint_DrawLine(x1, y1, x2, y2, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
            printf("Drew line from %d,%d to %d,%d\n", x1, y1, x2, y2);
        } else {
            printf("Invalid draw_line command format. Use: draw_line x1 y1 x2 y2\n");
        }
    } else if (screen_on && strncmp(cmd, "draw_rectangle", 14) == 0) {
        int x1, y1, x2, y2;
        if (sscanf(cmd, "draw_rectangle %d %d %d %d", &x1, &y1, &x2, &y2) == 4) {
            Paint_DrawRectangle(x1, y1, x2, y2, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
            printf("Drew rectangle from %d,%d to %d,%d\n", x1, y1, x2, y2);
        } else {
            printf("Invalid draw_rectangle command format. Use: draw_rectangle x1 y1 x2 y2\n");
        }
    } else if (screen_on && strncmp(cmd, "draw_circle", 11) == 0) {
        int x, y, r;
        if (sscanf(cmd, "draw_circle %d %d %d", &x, &y, &r) == 3) {
            Paint_DrawCircle(x, y, r, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
            printf("Drew circle at %d,%d with radius %d\n", x, y, r);
        } else {
            printf("Invalid draw_circle command format. Use: draw_circle x y r\n");
        }
    } else if (screen_on && strncmp(cmd, "draw_num", 8) == 0) {
        int x, y, num;
        if (sscanf(cmd, "draw_num %d %d %d", &x, &y, &num) == 3) {
            Paint_DrawNum(x, y, num, &Font16, BLACK, WHITE);
            printf("Drew number %d at %d,%d\n", num, x, y);
        } else {
            printf("Invalid draw_num command format. Use: draw_num x y num\n");
        }
    } else if (screen_on && strncmp(cmd, "set_pixel", 9) == 0) {
        int x, y, color;
        if (sscanf(cmd, "set_pixel %d %d %d", &x, &y, &color) == 3) {
            Paint_SetPixel(x, y, color);
            printf("Set pixel at %d,%d to color %d\n", x, y, color);
        } else {
            printf("Invalid set_pixel command format. Use: set_pixel x y color\n");
        }
    } else if (screen_on && strcmp(cmd, "close") == 0) {
        epaper_close();
        printf("e-Paper closed\n");
    } else if (screen_on && strcmp(cmd, "editor") == 0) {
        editor_mode = true;
        absolute_time_t time = get_absolute_time();
        editor_last_ts = to_ms_since_boot(time);
        printf("Editor mode enabled\n");
    } else {
        printf("Unknown command: %s\n", cmd);
    }
    
}

void core1_entry() {
    epaper_init();
    
    EPD_4IN2_V2_Display(BlackImage);

    char cmd_buffer[256];
    int i = 0;

    while (true) {
        bool newChar = false;
        while (!queue_is_empty(&q)) {
            int c;
            queue_remove_blocking(&q, &c);
            newChar = true;

            if(editor_mode){
                absolute_time_t time = get_absolute_time();
                editor_last_ts = to_ms_since_boot(time);
                if (c == '\n' || c == '\r') {
                    screen_log[screen_log_i++] = '\n';
                } else if (c == '\b' || c == 127) { // Handle backspace
                    if (screen_log_i > 0) {
                        screen_log_i--;
                    }
                } else {
                    if (screen_log_i < (sizeof(screen_log) - 1)) {
                        screen_log[screen_log_i++] = c;
                    }
                }
            } else {
                if (c == '\n' || c == '\r') {
                    cmd_buffer[i] = '\0';
                    process_command(cmd_buffer);
                    i = 0;
                    // Clear the command buffer display area
                    Paint_ClearWindows(1, 281, 399, 299, WHITE);
                    break;
                } else if (c == '\b' || c == 127) { // Handle backspace
                    if (i > 0) {
                        i--;
                    }
                } else {
                    if (i < (sizeof(cmd_buffer) - 1)) {
                        cmd_buffer[i++] = c;
                    }
                }
            }
        }
        if(newChar){
            if(!screen_on){
                epaper_init();
            }
            absolute_time_t time = get_absolute_time();
            screen_last_ts = to_ms_since_boot(time);
            if(editor_mode){
                bool overflow = false;
                Paint_Clear(WHITE);
                screen_log[screen_log_i] = '\0';
                overflow = Paint_DrawString_EN(10, 10, screen_log, current_font, WHITE, BLACK);
                if(overflow){
                    while(screen_log[screen_log_i] != '\n' || screen_log_i>1){
                        screen_log_i--;
                    }
                    if(screen_log_i!=0){
                        int new_i = 0;
                        while(screen_log[screen_log_i] != '\0'){
                            screen_log[new_i++] = screen_log[screen_log_i++];
                        }
                        screen_log_i = new_i;
                    }
                    Paint_Clear(WHITE);
                    // if overflow reset the screen_log buffer with only the last line
                    Paint_DrawString_EN(10, 10, screen_log, current_font, WHITE, BLACK);
                }
                EPD_4IN2_V2_PartialDisplay(BlackImage, 0, 0, 400, 300);
            } else {
                cmd_buffer[i] = '\0';
                // Update the command buffer display
                Paint_ClearWindows(2, 271, 399, 299, WHITE);
                Paint_DrawRectangle(1, 270, 400, 300, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
                // printf("Current: %s, buff:%s\n", text, cmd_buffer);
                Paint_DrawString_EN(10, 275, cmd_buffer, &Font16, WHITE, BLACK);
                EPD_4IN2_V2_PartialDisplay(BlackImage, 0, 0, 400, 300);
            }
            
        }
        if(screen_on){
            absolute_time_t time = get_absolute_time();
            uint32_t ms = to_ms_since_boot(time);
            if(screen_last_ts + 30000 < ms){
                printf("Turning off screen due to inactivity (30s)");
                epaper_close();
            }
        }
    }
}


int main()
{
    stdio_init_all();
    
    printf("e-ink api ready\n");
    
    queue_init(&q, sizeof(int), 256);
    
    multicore_launch_core1(core1_entry);

    while (true) {
        int c = getchar_timeout_us(0);
        if (c != PICO_ERROR_TIMEOUT) {
            queue_add_blocking(&q, &c);
            if (c == '\n' || c == '\r') {
                 printf("\n");
            } else if (c == '\b' || c == 127) {
                 printf("\b \b");
            } else {
                printf("%c", c);
            }
        }
        absolute_time_t time = get_absolute_time();
        uint32_t ms = to_ms_since_boot(time);
        if(editor_last_ts != 0 && editor_mode && (editor_last_ts + 10000 < ms)){
            printf("Disabling editor after 10s of inactivity");
            editor_mode = false;
        }
    }

    epaper_close();
    return 0;
}
