#include <SPI.h>
#include "7in5_epd.h"
#include "7in5_epd_spi.h"
#include "Ap_75demo.h"
#include "epd_gui.h"
void Canvas_Display_With_Red(void);
static uint8_t GetPixelColor_Display(uint16_t x, uint16_t y);
/***************************************************************************************
 * Project  :
 * Experimental Platform :Arduino ESP32 + 7.5inch E-Paper Display
 * Hardware Connection :Arduino ESP32-> 7.5inch E-Paper
 *      IO27 --> CS
 *      IO18 --> CLK
 *      IO23 --> MOSI
 *      IO14  --> DC
 *      IO33  --> RST
 *      IO13  --> BUSY
 * Web Site    :www.seengreat.com
***************************************************************************************/
/******************************
 * image :
 * 0------------> x (1~800) 
 * |
 * |
 * |
 * |
 * y (1~480)
 * 
 * */

/*Local canvas definition*/
#define CANVAS_WIDTH    400
#define CANVAS_HEIGHT   240
#define CANVAS_X        ((800 - CANVAS_WIDTH) / 2)   
#define CANVAS_Y        ((480 - CANVAS_HEIGHT) / 2)

static uint8_t canvasBuffer[CANVAS_WIDTH * CANVAS_HEIGHT / 8];

void setup() {
	pinMode(BUSY_Pin, INPUT);  //BUSY
	pinMode(RES_Pin, OUTPUT);  //RES
	pinMode(DC_Pin, OUTPUT);   //DC
	pinMode(CS_Pin, OUTPUT);   //CS
	//SPI
	SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
	SPI.begin();
}

void loop() {
 unsigned char i;
      EPD_Init(); //Full screen refresh initialization.
      EPD_WhiteScreen_White(); //Clear screen function.    
      EPD_DeepSleep(); //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
      delay_xms(2000); //Delay for 2s. 

        EPD_Init(); //Full screen refresh initialization.
        EPD_SetRAMValue_BaseMap(gImage_BW1,gImage_RW1);
        EPD_DeepSleep(); //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
        delay_xms(5000); //Delay for 5s.

        EPD_Init(); //Full screen refresh initialization.
        EPD_WhiteScreen_White(); //Clear screen function.    
        EPD_DeepSleep(); //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
        delay_xms(1000); //Delay for 1s. 
    
        EPD_Init(); //Full screen refresh initialization.
        Gui_Clear(WHITE); 
        EPD_Init_Part();
        EPD_Dis_Part(CANVAS_X, CANVAS_Y, gImage_1, CANVAS_HEIGHT, CANVAS_WIDTH);
        EPD_DeepSleep(); //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
        delay_xms(5000); //Delay for 5s.

        EPD_Init(); //Full screen refresh initialization.
        EPD_WhiteScreen_White(); //Clear screen function.    
        EPD_DeepSleep(); //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
        delay_xms(1000); //Delay for 1s. 
    

        EPD_Init(); //Electronic paper initialization.  
        EPD_WhiteScreen_White();
        EPD_Init_Part(); //Pa refresh initialization.
        for(i=0;i<6;i++)
        {
            EPD_Dis_Part_Time(240,180,Num[1],Num[0],gImage_numdot,Num[0],Num[i],5,104,48); //x,y,DATA-A~E,Resolution 48*104                 
        }       
        EPD_DeepSleep();  //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
                delay_xms(2000); //Delay for 2s.


        EPD_Init(); //Full screen refresh initialization.
        EPD_WhiteScreen_White(); //Clear screen function.
        EPD_DeepSleep(); //Enter the sleep mode and please do not delete it, otherwise it will reduce the lifespan of the screen.
        delay(1000); //Delay for 1s. 

           /*Initialize the canvas*/
        Image_Init(canvasBuffer, CANVAS_WIDTH, CANVAS_HEIGHT, ROTATE_0, WHITE);
        Gui_Clear(EPD_7IN5G_WHITE); 
        
        Gui_Draw_Rectangle(5, 10, 85, 110,EPD_7IN5G_RED,FULL,PIXEL_1X1);
        Gui_Draw_Rectangle(105,10,185,110,EPD_7IN5G_RED,EMPTY,PIXEL_1X1);
        Gui_Draw_Circle(245,60,40,EPD_7IN5G_BLACK,FULL,PIXEL_1X1);
        Gui_Draw_Circle(345,60,40,EPD_7IN5G_BLACK,EMPTY,PIXEL_1X1);

        Gui_Draw_Line(5, 140, 385, 140, EPD_7IN5G_BLACK, PIXEL_1X1, SOLID);
        Gui_Draw_Line(5, 150, 385, 150, EPD_7IN5G_BLACK, PIXEL_1X1, SOLID);
    
        Gui_Draw_Point(11, 175, EPD_7IN5G_BLACK, PIXEL_2X2, DOT_STYLE_DFT);
        Gui_Draw_Point(17, 175, EPD_7IN5G_BLACK, PIXEL_3X3, DOT_STYLE_DFT);
        Gui_Draw_Point(25, 175, EPD_7IN5G_BLACK, PIXEL_4X4, DOT_STYLE_DFT);
        Gui_Draw_Str(30, 160, "SEENGREAT", &Font20, EPD_7IN5G_WHITE, EPD_7IN5G_BLACK); //17*24		
        
            /*partial refresh*/
        EPD_Init_Part();
        EPD_Dis_Part(CANVAS_X, CANVAS_Y, canvasBuffer, CANVAS_HEIGHT, CANVAS_WIDTH);
        Canvas_Display_With_Red();
        
        EPD_DeepSleep();//EPD_DeepSleep,Sleep instruction is necessary, please do not delete!!!
        delay_xms(2000); //2s
        delay_xms(2000); //2s

      EPD_Init(); //Full screen refresh initialization.
      EPD_WhiteScreen_White(); //Clear screen function.
      delay_xms(1000); //Delay for 2s.
			
    while(1);  // The program stops here   
    /* USER CODE END WHILE */

}

uint8_t Validate_Canvas(uint16_t width, uint16_t height, uint16_t x, uint16_t y) {
    // Check if the total number of pixels exceeds the limit.
    if((width * height)/8 > 12000) {
        printf("Error: Canvas creation failed! Total pixels (%d) exceed limit (%d)\r\n", 
               width * height, 12000);
        return 0;
    }
    
    // Check if the boundary extends beyond the screen.
    if((x + width) > EPD_WIDTH || (y + height) > EPD_HEIGHT) {
        printf("Error: Canvas creation failed! Out of screen bounds (X:%d Y:%d W:%d H:%d)\r\n",
               x, y, width, height);
        return 0;
    }
    
    printf("Success: Canvas created (X:%d Y:%d W:%d H:%d) Total pixels:%d\r\n",
           x, y, width, height, width * height);
    return 1;
}

/*Add the desired red range that you want to achieve here*/
static uint8_t GetPixelColor_Display(uint16_t x, uint16_t y) {
		static const FONT* font = &Font20;
    if(x >= 5 && x <= 85 && y >= 10 && y <= 110) {
        return EPD_7IN5G_RED;
    }
		if(x >= 105 && x <= 185 && y >= 10 && y <= 110) {
        return EPD_7IN5G_RED;
    }
		if(x >= 30 && x <= 30 + 9*font->w && y >= 160 && y <= 160 + font->h) {
        return EPD_7IN5G_RED;
    }
    return EPD_7IN5G_BLACK;
}

void Canvas_Display_With_Red(void) {
    uint32_t i, j, k;
    uint16_t canvasX, canvasY;
    uint8_t byte, oldData, newData;
    
    EPD_Init();
    
    // Transfer old data (0x10) - Black/White data
    EPD_W21_WriteCMD(0x10);
    for(i = 0; i < EPD_HEIGHT; i++) {
        for(j = 0; j < EPD_WIDTH / 8; j++) {
            if(i >= CANVAS_Y && i < CANVAS_Y + CANVAS_HEIGHT && 
               j >= CANVAS_X / 8 && j < (CANVAS_X + CANVAS_WIDTH) / 8) {
                // Inside canvas area
               canvasX = j - CANVAS_X / 8;
               canvasY = i - CANVAS_Y;
               byte = canvasBuffer[canvasY * (CANVAS_WIDTH / 8) + canvasX];
                
                // For black/white data, we set all pixels to white (0xFF) except those that are black in canvas
                // Red pixels will also be set to white here (they'll be handled in the red data)
                oldData = ~byte; // Invert because 0 is white, 1 is black in the display
                EPD_W21_WriteDATA(oldData);
            } else {
                // Outside canvas area - white
                EPD_W21_WriteDATA(0xFF); // All white
            }
        }
    }
    
    // Transfer new data (0x13) - Red data
    EPD_W21_WriteCMD(0x13);
    for(i = 0; i < EPD_HEIGHT; i++) {
        for(j = 0; j < EPD_WIDTH / 8; j++) {
            if(i >= CANVAS_Y && i < CANVAS_Y + CANVAS_HEIGHT && 
               j >= CANVAS_X / 8 && j < (CANVAS_X + CANVAS_WIDTH) / 8) {
                // Inside canvas area
                canvasX = j - CANVAS_X / 8;
                canvasY = i - CANVAS_Y;
                byte = canvasBuffer[canvasY * (CANVAS_WIDTH / 8) + canvasX];
                
                // For red data, we need to check which pixels should be red
                // Here we use the GetPixelColor1 function to determine if a pixel should be red
                newData = 0;
                for(k = 0; k < 8; k++) {
                    uint16_t px = j*8 + k;
                    uint16_t py = i;
                    if(byte & (1 << (7 - k))) { // If pixel is set in canvas
                        if(GetPixelColor_Display(px - CANVAS_X+1, py - CANVAS_Y+1) == EPD_7IN5G_RED) {
                            newData |= (1 << (7 - k)); // Set red pixel
                        }
                    }
                }
                EPD_W21_WriteDATA(newData);
            } else {
                // Outside canvas area - no red
                EPD_W21_WriteDATA(0x00); // No red
            }
        }
    }
    
    EPD_Update();
}