#include "low_level_display_epaper.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Waveshare 4.2" e-Paper monochrome driver (supports partial refresh)
extern "C" {
    #include "EPD_4in2_V2.h"
    #include "DEV_Config.h"
}

// Note: EPD_4IN2_V2 is monochrome (black/white) and supports fast partial refresh

LowLevelDisplayEPaper::LowLevelDisplayEPaper(const epaper_config* cfg, int w, int h)
    : config(cfg), width(w), height(h), initialized(false), framebuffer(nullptr), dirty(false) {
    
    // Allocate framebuffer (1 bit per pixel for monochrome e-paper)
    int buffer_size = (width * height + 7) / 8;  // Round up to nearest byte
    framebuffer = (uint8_t*)malloc(buffer_size);
    if (framebuffer) {
        memset(framebuffer, 0xFF, buffer_size);  // White
    }
}

LowLevelDisplayEPaper::~LowLevelDisplayEPaper() {
    if (framebuffer) {
        free(framebuffer);
    }
}

bool LowLevelDisplayEPaper::init() {
    if (initialized) {
        return true;
    }
    
    if (!framebuffer) {
        printf("Failed to allocate framebuffer for e-paper display\n");
        return false;
    }
    
    printf("Initializing Waveshare 4.2\" e-Paper display (%dx%d)...\n", width, height);
    
    // Initialize Waveshare driver (handles GPIO and SPI setup)
    DEV_Module_Init();
    EPD_4IN2_V2_Init();
    
    printf("E-paper display initialized successfully (partial refresh enabled)\n");
    initialized = true;
    return true;
}

void LowLevelDisplayEPaper::clear(bool white) {
    if (!framebuffer) return;
    
    int buffer_size = (width * height + 7) / 8;
    memset(framebuffer, white ? 0xFF : 0x00, buffer_size);
    dirty = true;
}

void LowLevelDisplayEPaper::draw_pixel(int x, int y, bool white) {
    if (!framebuffer || x < 0 || x >= width || y < 0 || y >= height) {
        return;
    }
    
    int byte_index = (y * width + x) / 8;
    int bit_index = 7 - ((y * width + x) % 8);
    
    if (white) {
        framebuffer[byte_index] |= (1 << bit_index);
    } else {
        framebuffer[byte_index] &= ~(1 << bit_index);
    }
    
    dirty = true;
}

void LowLevelDisplayEPaper::draw_buffer(const uint8_t* bit_buffer) {
    if (!bit_buffer || !framebuffer) return;
    
    // Direct copy of 1-bit buffer
    int buffer_size = (width * height + 7) / 8;
    memcpy(framebuffer, bit_buffer, buffer_size);
    dirty = true;
}

void LowLevelDisplayEPaper::refresh() {
    if (!dirty || !framebuffer) {
        return;
    }
    
    if (!initialized) {
        printf("E-paper not initialized, cannot refresh\n");
        return;
    }
    
    // printf("Refreshing e-paper display (partial refresh)...\n");
    
    // Use partial refresh for fast updates (~1 second instead of ~15 seconds)
    // Partial refresh updates only the changed pixels
    EPD_4IN2_V2_PartialDisplay(framebuffer, 0, 0, EPD_4IN2_V2_WIDTH, EPD_4IN2_V2_HEIGHT);
    
    // printf("E-paper partial refresh complete\n");
    dirty = false;
}

void LowLevelDisplayEPaper::clear_display() {
    if (!initialized) {
        printf("E-paper not initialized, cannot clear\n");
        return;
    }
    
    printf("Clearing e-paper display...\n");
    EPD_4IN2_V2_Clear();
    
    // Also clear framebuffer
    clear(true);
    dirty = false;  // Already refreshed by EPD_4IN2_V2_Clear
}

void LowLevelDisplayEPaper::full_refresh() {
    if (!framebuffer || !initialized) {
        printf("E-paper not initialized or no framebuffer\n");
        return;
    }
    
    printf("Performing full refresh (removes ghosting, ~15 seconds)...\n");
    
    // Full refresh uses EPD_4IN2_V2_Display for complete screen update
    // This is slower but removes any ghosting from partial refreshes
    EPD_4IN2_V2_Display(framebuffer);
    
    printf("Full refresh complete\n");
    dirty = false;
}

void LowLevelDisplayEPaper::sleep() {
    if (!initialized) {
        return;
    }
    
    printf("Putting e-paper display to sleep...\n");
    EPD_4IN2_V2_Sleep();
}

void LowLevelDisplayEPaper::on_idle_2min() {
    // E-paper doesn't dim
}

void LowLevelDisplayEPaper::on_idle_10min() {
    if (!is_sleeping) {
        sleep();
        is_sleeping = true;
        printf("E-Paper: Entered sleep mode\n");
    }
}

void LowLevelDisplayEPaper::on_user_interaction() {
    if (is_sleeping) {
        printf("E-Paper: Waking from sleep...\n");
        init(); // Re-initialize to wake up
        is_sleeping = false;
        printf("E-Paper: Ready\n");
    }
}
