#include "lcd_gui.h"
#include "lcd_2inch.h"
#include "main.h" 
#include <string.h> 
#include "spi.h" 
extern SPI_HandleTypeDef hspi1;

void Gui_set_pix(uint16_t x, uint16_t y, uint16_t color)
{
    Draw_Point(x, y, color);
}

void Gui_draw_point(uint16_t x, uint16_t y, uint16_t color, uint16_t width)
{
    uint16_t xd, yd;

    if(x >= lcddev.width || y >= lcddev.height) return; 

    for(xd = 0; xd < width; xd++)
    {
        for(yd = 0; yd < width; yd++)
        {

            if(x + xd > lcddev.width || y + yd > lcddev.height) continue;
            Gui_set_pix(x + xd, y + yd, color);
        }
    }
}


void Gui_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color, uint16_t width, LINE_STYLE type)
{
    uint16_t x = x1;
    uint16_t y = y1;
    int dx = (x2 >= x1) ? (x2 - x1) : (x1 - x2);
    int dy = (y2 >= y1) ? (y2 - y1) : (y1 - y2);
    int p = dx - dy;
    int e2;
    int xdir = (x1 > x2) ? -1 : 1;
    int ydir = (y1 > y2) ? -1 : 1;
    int dot_cnt = 0;

    for(;;)
    {
        dot_cnt++;
        if(type == DOTTED && dot_cnt % 3 == 0)
        {
            Gui_draw_point(x, y, color, width);
        }
        else if(type == SOLID)
        {
            Gui_draw_point(x, y, color, width);
        }
        if(x == x2 && y == y2) break;
        e2 = 2 * p;
        if(e2 >= -dy) { p -= dy; x += xdir; }
        if(e2 <= dx) { p += dx; y += ydir; }
    }
}
void Gui_fill_color(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{
    uint16_t temp;
    if(x1 > x2) { temp = x1; x1 = x2; x2 = temp; }
    if(y1 > y2) { temp = y1; y1 = y2; y2 = temp; }

    Spi_write_cmd(lcddev.setxcmd);
    Spi_write_data(x1 >> 8); Spi_write_data(x1 & 0xFF);
    Spi_write_data(x2 >> 8); Spi_write_data(x2 & 0xFF);

    Spi_write_cmd(lcddev.setycmd);
    Spi_write_data(y1 >> 8); Spi_write_data(y1 & 0xFF);
    Spi_write_data(y2 >> 8); Spi_write_data(y2 & 0xFF);

    Spi_write_cmd(lcddev.wramcmd); 

    LCD_CS_0;
    LCD_DC_1;

    uint16_t w = x2 - x1 + 1;
    uint16_t h = y2 - y1 + 1;

    uint8_t color_hi = color >> 8;
    uint8_t color_lo = color & 0xFF;

    #define BUF_SIZE 64 
    uint8_t buf[BUF_SIZE * 2];

    for(int k = 0; k < BUF_SIZE; k++) {
        buf[k*2] = color_hi;
        buf[k*2+1] = color_lo;
    }

    uint32_t total_pixels = (uint32_t)w * h;
    uint32_t remaining = total_pixels;

    while(remaining > 0)
    {
        uint32_t chunk = (remaining > BUF_SIZE) ? BUF_SIZE : remaining;
        HAL_SPI_Transmit(&hspi1, buf, chunk * 2, 100);
        remaining -= chunk;
    }

    LCD_CS_1;
}

void Gui_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color, uint16_t width, FILL fill)
{
    if(fill == FULL)
    {
        Gui_fill_color(x1, y1, x2, y2, color);
    }
    else if(fill == EMPTY)
    {
        Gui_draw_line(x1, y1, x2, y1, color, width, SOLID);
        Gui_draw_line(x1, y1, x1, y2, color, width, SOLID);
        Gui_draw_line(x1, y2, x2, y2, color, width, SOLID);
        Gui_draw_line(x2, y1, x2, y2, color, width, SOLID);
    }
}


//Bresenham  algorithm draw circle
void Gui_draw_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color, uint16_t width, FILL fill)
{
    uint16_t dx=0,dy=r;
    int d = 1-r;
    uint16_t i;
    if(x >= lcddev.width || y >= lcddev.height)
    {
        printf("ERR:circle center point out of screen area!\r\n");
        return ;
    } 
    while(dy>dx)
    {
        if(fill==EMPTY)
        {
            Gui_draw_point(x+dx, y+dy, color, width);
            Gui_draw_point(x+dy, y+dx, color, width);
            Gui_draw_point(x-dx, y+dy, color, width);
            Gui_draw_point(x-dy, y+dx, color, width);
            Gui_draw_point(x-dx, y-dy, color, width);
            Gui_draw_point(x-dy, y-dx, color, width);
            Gui_draw_point(x+dx, y-dy, color, width);
            Gui_draw_point(x+dy, y-dx, color, width);
        }
        else if(fill==FULL)
        {
            for(i=dx;i<=dy;i++)
            {
                Gui_draw_point(x+dx, y+i, color, width);
                Gui_draw_point(x+i, y+dx, color, width);
                Gui_draw_point(x-dx, y+i, color, width);
                Gui_draw_point(x-i, y+dx, color, width);
                Gui_draw_point(x-dx, y-i, color, width);
                Gui_draw_point(x-i, y-dx, color, width);
                Gui_draw_point(x+dx, y-i, color, width);
                Gui_draw_point(x+i, y-dx, color, width);                
            }
        }
        if(d<0)
        {
            d += 2*dx+3;    
        }
        else
        {
            d += 2*(dx-dy)+5;
            dy--;
        }
        dx++;
    }
}


void Gui_draw_char(uint16_t x, uint16_t y, const char str_char, FONT* Font, uint16_t color, uint16_t background)
{
    uint16_t row, col_bit;
    uint16_t char_nbytes = Font->h * (Font->w / 8 + (Font->w % 8 ? 1 : 0));
    uint32_t offset = (str_char - ' ') * char_nbytes;
    const char *ptr = &Font->table[offset];

    for (row = 0; row < Font->h; row++)
    {
        for (col_bit = 0; col_bit < Font->w; col_bit++)
        {
            if (*ptr & (0x80 >> (col_bit % 8)))
                Gui_set_pix(x + col_bit, y + row, color);
            else if (background != 0xFFFF) 
                Gui_set_pix(x + col_bit, y + row, background);

            if (col_bit % 8 == 7) ptr++;
        }
        if (Font->w % 8 != 0) ptr++;
    }
}

void Gui_draw_str(uint16_t x, uint16_t y, const char *str_char, FONT* Font, uint16_t color, uint16_t background)
{
    uint16_t dx = x, dy = y;
    while(*str_char != '\0')
    {
        if((dx + Font->w) > lcddev.width) 
        {
            dx = x;
            dy += Font->h;
        }

        if((dy + Font->h) > lcddev.height) break;
        
        Gui_draw_char(dx, dy, *str_char++, Font, color, background);
        dx += Font->w;
    }
}
