#include "lcd_touch.h"
#include "lcd_2inch.h"
#include "lcd_gui.h"

tp_dev_t tp_dev;

// CST816 touch controller I2C address (shifted for 8-bit write)
#define CST816_ADDR (0x15 << 1)

// Menu area width and button geometry
#define SCREEN_W    320
#define SCREEN_H    240
#define MENU_W      80  
#define BTN_H       34 
#define GAP         4
#define BTN_START_X 5
#define BTN_W       (MENU_W - 10) 

// Y-coordinates of each button in the menu
#define Y_BTN1      5   // Clear
#define Y_BTN2      44  // Red
#define Y_BTN3      83  // Green
#define Y_BTN4      122 // Blue
#define Y_BTN5      161 // Black
#define Y_BTN6      200 // YELLOW


// SCL -> PB6
// SDA -> PB7

#define I2C_PORT      GPIOB
#define I2C_SCL_PIN   GPIO_PIN_6
#define I2C_SDA_PIN   GPIO_PIN_7

#define I2C_SCL_1     HAL_GPIO_WritePin(I2C_PORT, I2C_SCL_PIN, GPIO_PIN_SET)
#define I2C_SCL_0     HAL_GPIO_WritePin(I2C_PORT, I2C_SCL_PIN, GPIO_PIN_RESET)
#define I2C_SDA_1     HAL_GPIO_WritePin(I2C_PORT, I2C_SDA_PIN, GPIO_PIN_SET)
#define I2C_SDA_0     HAL_GPIO_WritePin(I2C_PORT, I2C_SDA_PIN, GPIO_PIN_RESET)
#define I2C_SDA_READ  HAL_GPIO_ReadPin(I2C_PORT, I2C_SDA_PIN)

void SI2C_Delay(void) {
    for(volatile int i=0; i<30; i++);
}

/**
 * @brief  Generate I2C start condition: SDA goes low while SCL is high.
 */
void SI2C_Start(void)
{
    I2C_SDA_1;
    I2C_SCL_1;
    SI2C_Delay();
    I2C_SDA_0;
    SI2C_Delay();
    I2C_SCL_0; 
}

/**
 * @brief  Generate I2C stop condition: SDA goes high while SCL is high.
 */
void SI2C_Stop(void)
{
    I2C_SCL_0;
    I2C_SDA_0;
    SI2C_Delay();
    I2C_SCL_1;
    SI2C_Delay();
    I2C_SDA_1;
    SI2C_Delay();
}

/**
 * @brief  Wait for an acknowledge from the slave device.
 * @return 0 if ACK received, 1 if timeout or NACK.
 */
uint8_t SI2C_WaitAck(void)
{
    uint8_t ucErrTime = 0;
    I2C_SDA_1;       // Release SDA
    SI2C_Delay();
    I2C_SCL_1;       // Clock pulse to sample ACK
    SI2C_Delay();
    
    while(I2C_SDA_READ)    // Wait for SDA to be pulled low (ACK)
    {
        ucErrTime++;
        if(ucErrTime > 250)
        {
            SI2C_Stop();
            return 1;     // Timeout or NACK
        }
    }
    I2C_SCL_0;     // End of ACK clock pulse
    return 0;      // ACK received
}

/**
 * @brief  Send one byte over I2C.
 * @param  txd: Byte to transmit.
 */
void SI2C_SendByte(uint8_t txd)
{
    uint8_t t;
    for(t=0; t<8; t++)
    {
			  // Set SDA according to the MSB of the byte
        if((txd & 0x80) >> 7) I2C_SDA_1;
        else I2C_SDA_0;
        
        txd <<= 1;    // Shift to next bit
        SI2C_Delay();
        I2C_SCL_1;
        SI2C_Delay();
        I2C_SCL_0;
        SI2C_Delay();
    }
}

/**
 * @brief  Read one byte from I2C.
 * @param  ack: 1 to send ACK after reading, 0 to send NACK.
 * @return Received byte.
 */
uint8_t SI2C_ReadByte(unsigned char ack)
{
    unsigned char i, receive = 0;
    I2C_SDA_1; 
    for(i=0; i<8; i++)
    {
        I2C_SCL_0;
        SI2C_Delay();
        I2C_SCL_1;     // Clock high-data stable
        receive <<= 1;
        if(I2C_SDA_READ) receive++;   // Read bit
        SI2C_Delay();
    }

    I2C_SCL_0;    // End of byte transfer
    if (!ack) I2C_SDA_1; // NACK
    else I2C_SDA_0;      // ACK
    
    SI2C_Delay();
    I2C_SCL_1;    // Clock pulse for ACK/NACK
    SI2C_Delay();
    I2C_SCL_0;
    
    return receive;
}

/**
 * @brief  Initialize the touch controller (CST816).
 *         Sets default paint color, configures I2C, and performs hardware reset.
 */
void TP_Init(void)
{
    tp_dev.paint_color = BLACK;

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_Delay(50);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_Delay(200); 
		GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

/**
 * @brief  Scan the touch controller for touch events.
 *         Reads touch coordinates via I2C and updates tp_dev structure.
 * @note   The CST816 provides up to 5 touch points; here only the first is used.
 */
void TP_Scan(void)
{
    // Start I2C communication to read touch data
    SI2C_Start();
	
		// Send device address + write 
    SI2C_SendByte(0x2A);
    if(SI2C_WaitAck()) { SI2C_Stop(); return; }

		// Send register address (0x01) to read touch data
    SI2C_SendByte(0x01);
    if(SI2C_WaitAck()) { SI2C_Stop(); return; }

		// Restart for read
    SI2C_Start();

    SI2C_SendByte(0x2B);
    if(SI2C_WaitAck()) { SI2C_Stop(); return; }

    uint8_t data[6];
    for(int i=0; i<5; i++) {
        data[i] = SI2C_ReadByte(1); // ACK
    }
    data[5] = SI2C_ReadByte(0); // NACK
    
    SI2C_Stop();

    uint8_t finger_num = data[1];
    
    if(finger_num > 0)
    {
        uint16_t raw_x = ((data[2] & 0x0F) << 8) | data[3];
        uint16_t raw_y = ((data[4] & 0x0F) << 8) | data[5];

        tp_dev.x[0] = raw_y;
        tp_dev.y[0] = raw_x; 
        
        tp_dev.statu = TP_PRESS_DOWN;		// Touch pressed

    }
    else
    {
        tp_dev.statu = 0;		// No touch
    }
}

/**
 * @brief  Draw the board UI: left menu with color buttons and clear button,
 *         and a vertical separator line.
 */
void Load_Draw_Board(void)
{
    LCD_Clear(WHITE);

    // [1] CLEAR
    Gui_fill_color(BTN_START_X, Y_BTN1, BTN_START_X+BTN_W, Y_BTN1+BTN_H, BLUE);
    Gui_draw_str(BTN_START_X+18, Y_BTN1+10, "CLR", &Font16, RED, BLUE);

    // [2] RED
    Gui_fill_color(BTN_START_X, Y_BTN2, BTN_START_X+BTN_W, Y_BTN2+BTN_H, RED);

    // [3] GREEN
    Gui_fill_color(BTN_START_X, Y_BTN3, BTN_START_X+BTN_W, Y_BTN3+BTN_H, GREEN);

    // [4] BLUE
    Gui_fill_color(BTN_START_X, Y_BTN4, BTN_START_X+BTN_W, Y_BTN4+BTN_H, BLUE);

    // [5] BLACK
    Gui_fill_color(BTN_START_X, Y_BTN5, BTN_START_X+BTN_W, Y_BTN5+BTN_H, BLACK);

    // [6] YELLOW
    Gui_fill_color(BTN_START_X, Y_BTN6, BTN_START_X+BTN_W, Y_BTN6+BTN_H, YELLOW);

    Gui_draw_line(MENU_W, 0, MENU_W, SCREEN_H, BLACK, 1, SOLID);
}

/**
 * @brief  Main touch test function: scans touch and handles drawing/color selection.
 * @note   This function should be called repeatedly in the main loop.
 */
void TP_Test(void)
{
    TP_Scan();			// Check for touch events

    if (tp_dev.statu & TP_PRESS_DOWN)			//touch screen is pressed
    {
				// If touch is inside the menu area (x < MENU_W)
        if (tp_dev.x[0] < MENU_W)
        {
            HAL_Delay(150); 
						 // Determine which button was pressed based on Y coordinate
             if (tp_dev.y[0] > Y_BTN1 && tp_dev.y[0] < Y_BTN1+BTN_H) { // Clear
               // Clear the drawing area (right side)
							 Gui_fill_color(MENU_W+1, 0, SCREEN_W-1, SCREEN_H-1, WHITE);
            }
            else if (tp_dev.y[0] > Y_BTN2 && tp_dev.y[0] < Y_BTN2+BTN_H) tp_dev.paint_color = RED;
            else if (tp_dev.y[0] > Y_BTN3 && tp_dev.y[0] < Y_BTN3+BTN_H) tp_dev.paint_color = GREEN;
            else if (tp_dev.y[0] > Y_BTN4 && tp_dev.y[0] < Y_BTN4+BTN_H) tp_dev.paint_color = BLUE;
            else if (tp_dev.y[0] > Y_BTN5 && tp_dev.y[0] < Y_BTN5+BTN_H) tp_dev.paint_color = BLACK;
            else if (tp_dev.y[0] > Y_BTN6 && tp_dev.y[0] < Y_BTN6+BTN_H) tp_dev.paint_color = YELLOW;
        }
        else 
        {
					  // Touch in drawing area: draw a point (size 4) with current color
            Gui_draw_point(tp_dev.x[0], tp_dev.y[0], tp_dev.paint_color, 4);
        }
    }
}
