#include "lcd_2inch.h"

_lcd_dev lcddev;
uint16_t POINT_COLOR = RED;
uint16_t BACK_COLOR = WHITE;

// GPIO initialization
void Lcd_Gpio_Init(void)
{
    pinMode(LCD_CS, OUTPUT);
    pinMode(LCD_DC, OUTPUT);
    pinMode(LCD_RST, OUTPUT);
    pinMode(LCD_BL, OUTPUT);

    // Set initial levels
    LCD_CS_1;
    LCD_DC_1;
    LCD_RST_1;
    LCD_BL_1; // Backlight on by default

    // SPI initialization
    SPI.begin();
    SPI.setClockDivider(SPI_CLOCK_DIV2); 
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
}

// Hardware reset
static void Lcd_Reset(void)
{
    LCD_RST_0;
    delay(50);
    LCD_RST_1;
    delay(50);
}

// Write command
static void Spi_write_cmd(uint8_t data)
{
    LCD_CS_0;
    LCD_DC_0; // Command mode
    SPI.transfer(data);
    LCD_CS_1;
}

// Write data (8-bit)
static void Spi_write_data(uint8_t data)
{
    LCD_CS_0;
    LCD_DC_1; // Data mode
    SPI.transfer(data);
    LCD_CS_1;
}

// Write data (16-bit)
void Spi_write_word(uint16_t data)
{
    LCD_CS_0;
    LCD_DC_1;
    SPI.transfer(data >> 8);   // High byte
    SPI.transfer(data & 0xFF); // Low byte
    LCD_CS_1;
}

void Lcd_Init(void)
{
    Lcd_Reset();
    
    // ST7789 initialization sequence
    Spi_write_cmd(0x11); 
    delay(120);
    
    Spi_write_cmd(0x36); 
    Spi_write_data(0x00);
    Spi_write_cmd(0x3A); 
    Spi_write_data(0x05);
    
    Spi_write_cmd(0xB2);
    Spi_write_data(0x0C); 
    Spi_write_data(0x0C); 
    Spi_write_data(0x00);
    Spi_write_data(0x33); 
    Spi_write_data(0x33);

    Spi_write_cmd(0xB7); 
    Spi_write_data(0x35);
    Spi_write_cmd(0xBB); 
    Spi_write_data(0x19);
    Spi_write_cmd(0xC0); 
    Spi_write_data(0x2C);
    Spi_write_cmd(0xC2); 
    Spi_write_data(0x01);
    Spi_write_cmd(0xC3); 
    Spi_write_data(0x12);
    Spi_write_cmd(0xC4); 
    Spi_write_data(0x20);
    Spi_write_cmd(0xC6); 
    Spi_write_data(0x0F);
    Spi_write_cmd(0xD0); 
    Spi_write_data(0xA4); 
    Spi_write_data(0xA1);

    Spi_write_cmd(0xE0);
    Spi_write_data(0xD0); 
    Spi_write_data(0x04); 
    Spi_write_data(0x0D);
    Spi_write_data(0x11); 
    Spi_write_data(0x13); 
    Spi_write_data(0x2B);
    Spi_write_data(0x3F); 
    Spi_write_data(0x54); 
    Spi_write_data(0x4C);
    Spi_write_data(0x18); 
    Spi_write_data(0x0D); 
    Spi_write_data(0x0B);
    Spi_write_data(0x1F); 
    Spi_write_data(0x23);

    Spi_write_cmd(0xE1);
    Spi_write_data(0xD0); 
    Spi_write_data(0x04); 
    Spi_write_data(0x0C);
    Spi_write_data(0x11); 
    Spi_write_data(0x13); 
    Spi_write_data(0x2C);
    Spi_write_data(0x3F); 
    Spi_write_data(0x44); 
    Spi_write_data(0x51);
    Spi_write_data(0x2F); 
    Spi_write_data(0x1F); 
    Spi_write_data(0x1F);
    Spi_write_data(0x20); 
    Spi_write_data(0x23);

    Spi_write_cmd(0x21); 
    Spi_write_cmd(0x29); 
    
    LCD_Display_Dir(DFT_SCAN_DIR);
    analogWrite(LCD_BL, 200); 
    LCD_Clear(WHITE);
}

// Set cursor position
void LCD_Set_Cursor(uint16_t Xpos, uint16_t Ypos)
{
    Set_Window(Xpos, Ypos, Xpos, Ypos);
}

void LCD_Display_Dir(uint8_t dir)
{
    uint8_t regval = 0;
    uint8_t dir_reg = 0;
    uint16_t temp;

    lcddev.dir = dir;

    // 0-3: Portrait modes
    // 4-7: Landscape modes
    if (dir >= 4) 
    {
        lcddev.width = 320;
        lcddev.height = 240;
    } 
    else 
    {
        lcddev.width = 240;
        lcddev.height = 320;
    }
    // ST7789 register definitions:
    // Bit7: MY (Row Address Order)
    // Bit6: MX (Column Address Order)
    // Bit5: MV (Row/Column Exchange)
    switch (dir)
    {
        case L2R_U2D: // 0: 0 degrees (portrait)
            regval = 0x00; 
            break;
        case L2R_D2U: // 1: 0 degrees mirrored
            regval = 0x80; // MY=1
            break;
        case R2L_U2D: // 2: 180 degrees mirrored
            regval = 0x40; // MX=1
            break;
        case R2L_D2U: // 3: 180 degrees (portrait flipped)
            regval = 0xC0; // MY=1, MX=1
            break;
            
        case U2D_L2R: // 4: 90 degrees mirrored
            regval = 0x20; // MV=1
            break;
        case U2D_R2L: // 5: 90 degrees (landscape)
            regval = 0x60; // MV=1, MX=1
            break;
        case D2U_L2R: // 6: 270 degrees (landscape flipped)
            regval = 0xA0; // MV=1, MY=1
            break;
        case D2U_R2L: // 7: 270 degrees mirrored
            regval = 0xE0; // MV=1, MX=1, MY=1
            break;
            
        default:
            regval = 0x00;
            break;
    }

    // Write to register
    Spi_write_cmd(0x36);
    Spi_write_data(regval);

    Set_Window(0, 0, lcddev.width, lcddev.height);
}

void Set_Window(uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd)
{
    Spi_write_cmd(0x2A);
    Spi_write_data(xStar >> 8); Spi_write_data(xStar & 0xFF);
    Spi_write_data((xEnd - 1) >> 8); Spi_write_data((xEnd - 1) & 0xFF);

    Spi_write_cmd(0x2B);
    Spi_write_data(yStar >> 8); Spi_write_data(yStar & 0xFF);
    Spi_write_data((yEnd - 1) >> 8); Spi_write_data((yEnd - 1) & 0xFF);

    Spi_write_cmd(0x2C);
}

void LCD_Clear(uint16_t color)
{
    uint32_t total_point = (uint32_t)lcddev.width * (uint32_t)lcddev.height;
    Set_Window(0, 0, lcddev.width, lcddev.height);
    LCD_CS_0;
    LCD_DC_1;
    uint8_t color_high = color >> 8;
    uint8_t color_low = color & 0xFF;

    for(uint32_t i = 0; i < total_point; i++)
    {
         SPI.transfer(color_high);
         SPI.transfer(color_low);
    }
    LCD_CS_1;
}

// Interface function for GUI library
void Draw_Point(uint16_t x, uint16_t y, uint16_t color)
{
    Set_Window(x, y, x+1, y+1);
    Spi_write_word(color);
}


