#include <valarray>
#include "tft.h"
#include "ascii_font.h"


#ifdef HZK_FONT
#include "font_chuc.h"
#endif

#ifdef TFT_ST7735
#include "ST7735.h"
#endif

#ifdef TFT_ST7796
#include "ST7796.h"
#endif

const uint8_t initcmd[] = {0};
TFT_Color_t POINT_COLOR;
TFT_Color_t BACK_COLOR;
uint8_t *cmdIndex;
void TFT_Init(TFT_T *dev) {
    if(dev == NULL)
    {
        return;
    }
    uint8_t count, temp;
    cmdIndex = (uint8_t *) initcmd;
#ifdef TFT_ST7735
    if (dev->id==ST7735_ID) {
        cmdIndex= (uint8_t *) st7735initcmd;
        dev->setxcmd = ST7735_CASET;
        dev->setycmd = ST7735_RASET;
        dev->wgramcmd = ST7735_WRITE_RAM;
    }
#endif

#ifdef TFT_ST7735
    if (dev->id==ST7796_ID) {
        cmdIndex= (uint8_t *) st7796initcmd;
        dev->setxcmd = ST7796_CASET;
        dev->setycmd = ST7796_RASET;
        dev->wgramcmd = ST7796_WRITE_RAM;
    }
#endif
    while (*cmdIndex) {
        temp = *cmdIndex++;
        count = temp & 0x7F;
        dev->writeReg(*cmdIndex,cmdIndex + 1, count - 1);
        cmdIndex += count;

        if (0x81 == temp) {
            dev->delay(150);
        }
    }
    TFT_SetDir(dev,dev->dir);
}

uint32_t TFT_ReadID(TFT_T *dev) {
    uint32_t id = 0;
#ifdef TFT_ST7735
    ST7735_ReadID(dev, id);
#endif

#ifdef TFT_ST7796
    ST7796_ReadID(dev, id);
#endif
    return id;
}

void TFT_SetDir(TFT_T *dev, TFT_DIR_T dir) {
    uint8_t tmp;
#ifdef TFT_ST7735
    ST7735_SetDir(dev, dir);
#endif
#ifdef TFT_ST7796
    ST7796_SetDir(dev, dir);
#endif
}

void TFT_SetCur(TFT_T *dev, uint16_t x, uint16_t y) {
#ifdef TFT_ST7735
    ST7735_OFFSET(dev,x,y);
#endif

#ifdef TFT_ST7796
    ST7796_OFFSET(dev,x,y);
#endif
    uint8_t tmp[2];
    tmp[0] = x >> 8U;
    tmp[1] = x & 0xFFU;
    dev->writeReg(dev->setxcmd,tmp, 2);

    tmp[0] = y >> 8U;
    tmp[1] = y & 0xFFU;
    dev->writeReg(dev->setycmd,tmp, 2);

    tmp[0] = 0;
    dev->writeReg(dev->wgramcmd,tmp,0);
}

void TFT_SetColor(uint16_t pcolor,uint16_t bcolor){
    POINT_COLOR.color=pcolor;
    BACK_COLOR.color=bcolor;
}

void TFT_SetWindow(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
#ifdef TFT_ST7735
    ST7735_OFFSET(dev,x,y);
#endif

#ifdef TFT_ST7796
    ST7796_OFFSET(dev,x,y);
#endif
    uint8_t tmp[4];
    tmp[0] = x >> 8U;
    tmp[1] = x & 0xFFU;
    tmp[2] = (x + width - 1) >> 8U;
    tmp[3] = (x + width - 1) & 0xFFU;
    dev->writeReg(dev->setxcmd,tmp, 4);

    tmp[0] = y >> 8U;
    tmp[1] = y & 0xFFU;
    tmp[2] = (y + height - 1) >> 8U;
    tmp[3] = (y + height - 1) & 0xFFU;
    dev->writeReg(dev->setycmd,tmp, 4);

    tmp[0] = 0;
    dev->writeReg(dev->wgramcmd,tmp,0);
}

TFT_Color_t TFT_GetPixel(TFT_T *dev,uint16_t x, uint16_t y){
    uint8_t pixel_lsb, pixel_msb;
    uint8_t tmp;
    TFT_Color_t rec;
    rec.color=0;
    /* Set Cursor */
    TFT_SetCur(dev,x,y);
#ifdef TFT_ST7735
    ST7735_ReadRam(dev,rec);
#endif

#ifdef TFT_ST7796
    ST7796_ReadRam(dev,rec);
#endif
    return rec;
}

void TFT_SetPixel(TFT_T *dev,uint16_t x, uint16_t y,TFT_Color_t color){
    if((x>=dev->width) || (y >= dev->height)){ return;}
    TFT_SetCur(dev,x,y);
    dev->sendData(color.u8, 2);
}

void TFT_SetBacklight(TFT_T *dev,uint8_t value){
    dev->blacklight(value);
}

void TFT_DrawHLineColor(TFT_T *dev, uint16_t x, uint16_t y, uint16_t len,TFT_Color_t *color){
    if(x+len>dev->width)return;
    TFT_SetCur(dev,x,y);
    dev->sendData(color->u8,len*2);
}

void TFT_FillRect(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height,TFT_Color_t *color){
    if(x+width>dev->width||y+height>dev->height)return;
    uint32_t i;
    TFT_SetWindow(dev,x,y,width,height);
    for (i = 0; i < height; ++i) {
        TFT_DrawHLineColor(dev,x,y+i,width,color);
        color+=width;
    }
//    dev->sendData(color->u8,  size*2);
}

void TFT_DrawHLine(TFT_T *dev, uint16_t x, uint16_t y, uint16_t len,TFT_Color_t color){
    uint16_t i;
    uint8_t pdata[dev->width*2];
    if(x+len>dev->width)return;
    TFT_SetCur(dev,x,y);
    for(i = 0; i < len; i++)
    {
        /* Exchange LSB and MSB to fit LCD specification */
        pdata[2U*i] = color.u8[0];
        pdata[(2U*i) + 1U] =  color.u8[1];
//      pdata[(2U*i) + 1U] = (uint8_t)(Color >> 8);
//      pdata[2U*i] = (uint8_t)(Color);
    }
    dev->sendData(pdata,len*2);
}

void TFT_Fill(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height,TFT_Color_t color){
    if(x+width>dev->width||y+height>dev->height)return;
    uint32_t i;
    TFT_SetWindow(dev,x,y,width,height);
    for (i=0;i<height;i++){
        TFT_DrawHLine(dev,x,y+i,width,color);
    }
}

void TFT_FillColor(TFT_T *dev,TFT_Color_t color){
    dev->sendData(color.u8, 2);
}

#define swap_(a, b)    (a=(a)+(b),b=(a)-(b),a=(a)-(b))

void TFT_DrawLine(TFT_T *dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
    uint16_t i;
    if ((x1 < 0) || (x2 > dev->width) || (y1 < 0) || (y2 > dev->height))return;
    if (x1 == x2)    //画竖线
    {
        for (i = 0; i < (y2 - y1); i++) {
            TFT_SetPixel(dev, x1, y1 + i,POINT_COLOR);
        }
    } else if (y1 == y2)   //画横线
    {
        for (i = 0; i < (x2 - x1); i++) {
            TFT_SetPixel(dev, x1 + i, y1,POINT_COLOR);
        }
    } else      //画斜线
    {//Bresenham算法
        int p, twoDy, twoDyMinusDx, s1, s2;
        int dx = abs(x2 - x1), dy = abs(y2 - y1);
        if (dy > dx)    //斜率大于1
        {
            p = 2 * dx - dy;
            twoDy = 2 * dx;
            twoDyMinusDx = 2 * (dx - dy);
            if (y1 > y2)//斜率为负时 反转斜率
            {
                swap_(x1, x2);
                swap_(y1, y2);
            }
            s1 = x2 > x1 ? 1 : -1;
            TFT_SetPixel(dev, x1, y1, POINT_COLOR);
            while (y1 < y2) {
                y1++;
                if (p < 0) { p += twoDy; }
                else {
                    x1 += s1;
                    p += twoDyMinusDx;
                }
                TFT_SetPixel(dev, x1, y1, POINT_COLOR);
            }
        } else {
            p = 2 * dy - dx;
            twoDy = 2 * dy;
            twoDyMinusDx = 2 * (dy - dx);
            if (x1 > x2)//斜率为负时 反转斜率
            {
                swap_(x1, x2);
                swap_(y1, y2);
            }
            s2 = y2 > y1 ? 1 : -1;
            TFT_SetPixel(dev, x1, y1, POINT_COLOR);
            while (x1 < x2) {
                x1++;
                if (p < 0) { p += twoDy; }
                else {
                    y1 += s2;
                    p += twoDyMinusDx;
                }
                TFT_SetPixel(dev, x1, y1, POINT_COLOR);
            }
        }
    }
}

void TFT_DrawRect(TFT_T *dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
    TFT_DrawLine(dev, x1, y1, x2, y1); // Top side
    TFT_DrawLine(dev, x1, y1, x1, y2); // Left side
    TFT_DrawLine(dev, x2, y1, x2, y2); // Right side
    TFT_DrawLine(dev, x1, y2, x2, y2); // Bottom side
}

void TFT_DrawCircle(TFT_T *dev, uint16_t x0, uint16_t y0, uint16_t radius) {
    int x = radius;
    int y = 0;
    int decision = 1 - x;

    while (y <= x) {
        TFT_SetPixel(dev, x0 + x, y0 + y, POINT_COLOR);
        TFT_SetPixel(dev, x0 + y, y0 + x, POINT_COLOR);
        TFT_SetPixel(dev, x0 - y, y0 + x, POINT_COLOR);
        TFT_SetPixel(dev, x0 - x, y0 + y, POINT_COLOR);
        TFT_SetPixel(dev, x0 - x, y0 - y, POINT_COLOR);
        TFT_SetPixel(dev, x0 - y, y0 - x, POINT_COLOR);
        TFT_SetPixel(dev, x0 + y, y0 - x, POINT_COLOR);
        TFT_SetPixel(dev, x0 + x, y0 - y, POINT_COLOR);

        y++;

        if (decision <= 0) {
            decision += 2 * y + 1;
        } else {
            x--;
            decision += 2 * (y - x) + 1;
        }
    }
}

#define PI 3.14159265359

#define PI 3.14159265359

//void TFT_DrawArc(TFT_T *dev, uint16_t x0, uint16_t y0, uint16_t radius, int start_angle, int end_angle) {
//    int x = radius;
//    int y = 0;
//    int decision = 1 - x;
//    int change = 4 * (1 - x);
//
//    int angle = start_angle;
//
//    while (y <= x) {
//        if (angle >= start_angle && angle <= end_angle) {
//            TFT_SetPixel(dev, x0 + x, y0 + y, POINT_COLOR);
//            TFT_SetPixel(dev, x0 + y, y0 + x, POINT_COLOR);
//            TFT_SetPixel(dev, x0 - y, y0 + x, POINT_COLOR);
//            TFT_SetPixel(dev, x0 - x, y0 + y, POINT_COLOR);
//            TFT_SetPixel(dev, x0 - x, y0 - y, POINT_COLOR);
//            TFT_SetPixel(dev, x0 - y, y0 - x, POINT_COLOR);
//            TFT_SetPixel(dev, x0 + y, y0 - x, POINT_COLOR);
//            TFT_SetPixel(dev, x0 + x, y0 - y, POINT_COLOR);
//        }
//
//        if (decision <= 0) {
//            decision += 2 * y + 1;
//            change += 2;
//        } else {
//            x--;
//            decision += 2 * (y - x) + 1;
//            change += 4 * ((y - x) + 1);
//        }
//
//        y++;
//        angle = (int)(0.5 + (atan((double)y / x) * 180.0 / PI));
//    }
//}

#define PI 3.14159265359

void TFT_DrawArc(TFT_T *dev, uint16_t x, uint16_t y, uint16_t radius, int start_angle, int end_angle) {
    // 修复输入角度范围
    if (start_angle < 0) {
        start_angle = 0;
    } else if (start_angle > 360) {
        start_angle = 360;
    }
    if (end_angle < 0) {
        end_angle = 0;
    } else if (end_angle > 360) {
        end_angle = 360;
    }
    double angle;
    unsigned int cirx, ciry, lastX, lastY;
    cirx = x;
    ciry = y;

    angle = start_angle;
    while (angle <= end_angle) {
        lastX = cirx + radius * sin(angle * PI / 180);
        lastY = ciry - radius * cos(angle * PI / 180);
        TFT_SetPixel(dev, lastX, lastY, POINT_COLOR);
        angle = angle + 0.1;
    }
}

void TFT_ShowChar(TFT_T *dev, uint16_t x, uint16_t y, uint8_t chr, uint16_t size,bool mode) {
    uint16_t i, m, temp, size2, chr1;
    uint16_t ys = y;
    size2 = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);  //得到字体一个字符对应点阵集所占的字节数
    chr1 = chr - ' ';  //计算偏移后的值
    for (i = 0; i < size2; i++) {
        if (size == 12) { temp = asc2_1206[chr1][i]; } //调用1206字体
        else if (size == 16) { temp = asc2_1608[chr1][i]; } //调用1608字体
        else if (size == 24) { temp = asc2_2412[chr1][i]; } //调用2412字体
        else return;
        for (m = 0; m < 8; m++)           //写入数据
        {
            if (temp & 0x80)TFT_SetPixel(dev, x, ys,POINT_COLOR);
            else if(!mode) TFT_SetPixel(dev, x, ys,BACK_COLOR);
            temp <<= 1;
            ys++;
            if ((ys - y) == size) {
                ys = y;
                x++;
                break;
            }
        }
    }
}

void TFT_ShowString(TFT_T *dev, uint16_t x, uint16_t y, uint8_t *str, uint16_t size,bool mode) {
    while ((*str >= ' ') && (*str <= '~'))//判断是不是非法字符!
    {
        TFT_ShowChar(dev, x, y, *str, size,mode);
        x += size / 2;
        if (x > dev->width - size)  //换行
        {
            x = 0;
            y += size;
        }
        str++;
    }
}
#define GET_BIT(x, bit)    ((x & (1 << bit)) >> bit)

void TFT_HBSet(TFT_T *dev, uint8_t x, uint8_t y, uint8_t data, bool mode) {
    for (uint8_t i = 0; i < 8; i++) {
        if(GET_BIT(data, i)){
            TFT_SetPixel(dev, x + 7 - i, y, POINT_COLOR);
        } else if(!mode){
            TFT_SetPixel(dev, x + 7 - i, y, BACK_COLOR);
        }
    }
}
#ifdef HZK_FONT

void TFT_ShowCHChr(TFT_T *dev, uint8_t x, uint8_t y, Chinese_t *hzk, bool mode) {
    uint8_t ziku_bytes = ((Hzk_size + 7) / 8 * 8) / 8;
    for (uint8_t i = 0, ziku_byte_n = 0, size2 = Hzk_size * ziku_bytes; i < size2; i++) {
        TFT_HBSet(dev, x + ziku_byte_n * 8, y, hzk->data[i], mode);
        if (++ziku_byte_n >= ziku_bytes) {
            ziku_byte_n = 0;
            y++;
        }
    }
}
#ifdef UTF8_TO_UNICODE

#include "font_t.h"

#define    GET_LOW_BYTE0(x)    ((x >>  0) & 0x000000ff)    /* 获取第0个字节 */
#define    GET_LOW_BYTE1(x)    ((x >>  8) & 0x000000ff)    /* 获取第1个字节 */

void TFT_ShowCHString(TFT_T *dev, uint16_t x, uint16_t y, uint8_t *str,bool mode) {
    uint32_t unicode_letter;
    uint16_t step;
    while (*str != '\0') {
        bool found = false;
        step = Font_utf8_to_unicode(str, &unicode_letter);
        if (step != 0) {
            Chinese_t *hzk = find_chinese_data(GET_LOW_BYTE1(unicode_letter), GET_LOW_BYTE0(unicode_letter));
            if(hzk!=NULL){
                TFT_ShowCHChr(dev, x, y, hzk, mode);
                x += Hzk_size;
                str += step;
                found = true;
            }
        }
        if (!found) {
            TFT_ShowChar(dev, x, y, *str, Hzk_size,1);
            x += Hzk_size / 2;
            str++;
        }
        if (x > dev->width - Hzk_size) {
            x = 0;
            y += Hzk_size;
        }
    }
}

#else
void TFT_ShowCHString(TFT_T *dev, uint16_t x, uint16_t y, uint8_t *str,bool mode) {
    uint16_t index;
    while (*str != '\0') {
        bool found = false;
        for (index = 0; index < sizeof(Hzk) / sizeof(Chinese_t); index++) {
            if (Hzk[index].unicode[0] == str[0] && Hzk[index].unicode[1] == str[1]) // 对比汉字区码位码
            {
                TFT_ShowCHChr(dev, x, y, &Hzk[index],mode);
                x += Hzk_size;
                str++;
                str++;
                found = true;
                break;
            }
        }
        if (!found) {
            TFT_ShowChar(dev, x, y, *str, Hzk_size,mode);
            x += Hzk_size / 2;
            str++;
        }
        if (x > dev->width - Hzk_size) {
            x = 0;
            y += Hzk_size;
        }
    }
}
#endif
#endif
#ifdef LVGL_FONT
#include "font.h"
void draw_rect(Font_f_t *fd, uint16_t *data, size_t len) {
    TFT_T *dev=(TFT_T*)fd->dev;
    TFT_Color_t tmp;
    TFT_SetCur(dev,fd->x,fd->y);
    while (len--){
        tmp.color=*data;
        TFT_FillColor(dev,tmp);
        data++;
    }
}

void TFT_DisplayString(TFT_T *dev, const lv_font_t *font, uint8_t *s, uint16_t x, uint16_t y,bool mode) {
    uint32_t unicode_letter;
    Font_f_t fd = {.dev=dev, .dev_w=dev->width, .show=draw_rect};
    while (*s) {
        if (font) {
            s += Font_utf8_to_unicode(s, &unicode_letter);
            x += Font_draw_letter(font, &fd, unicode_letter, x, y);
            x += 0; //字间距
        } else {
            TFT_ShowChar(dev, x, y, *s, 12,mode);
            x += 12;
            s++;
        }
    }
}
#endif

uint32_t TFT_Pow(uint16_t m, uint16_t n) {
    uint32_t result = 1;
    while (n--) {
        result *= m;
    }
    return result;
}

void TFT_ShowNum(TFT_T *dev, uint16_t x, uint16_t y, uint32_t num, uint16_t len, uint8_t size1) {
    uint16_t t, temp;
    for (t = 0; t < len; t++) {
        temp = (num / TFT_Pow(10, len - t - 1)) % 10;
        if (temp == 0) {
            TFT_ShowChar(dev, x + (size1 / 2) * t, y, '0', size1,1);
        } else {
            TFT_ShowChar(dev, x + (size1 / 2) * t, y, temp + '0', size1,1);
        }
    }
}

void TFT_ShowPic(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height, TFT_Color_t *bmp) {
    if(x+width>dev->width||y+height>dev->height)return;
    uint32_t j;
    for(j = 0; j < height; j++)
    {
        TFT_DrawHLineColor(dev,x,y+j,width, bmp);
        bmp+=width;
    }
}

void TFT_DrawCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r) {
    TFT_DrawLine(dev, x - r, y, x + r, y);
    TFT_DrawLine(dev, x, y - r, x, y + r);
}

void TFT_DrawXCross(TFT_T *dev, uint16_t x, uint16_t y, uint8_t r) {
    TFT_DrawLine(dev, x - r, y - r, x + r, y + r);
    TFT_DrawLine(dev, x - r, y + r, x + r, y - r);
}

void TFT_DrawRoundedRect(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t radius) {
    // 画四条直线
    TFT_DrawLine(dev, x + radius, y, x + width - radius, y); // 上方横线
    TFT_DrawLine(dev, x + radius, y + height, x + width - radius, y + height); // 下方横线

    TFT_DrawLine(dev, x, y + radius, x, y + height - radius); // 左侧竖线
    TFT_DrawLine(dev, x + width, y + radius, x + width, y + height - radius); // 右侧竖线

    // 画四个圆角
    TFT_DrawArc(dev, x + radius, y + radius, radius, 270, 360); // 左上角
    TFT_DrawArc(dev, x + width - radius, y + radius, radius, 0, 90); // 右上角
    TFT_DrawArc(dev, x + width - radius, y + height - radius, radius, 90, 180); // 右下角
    TFT_DrawArc(dev, x + radius, y + height - radius, radius, 180, 270); // 左下角
}

uint16_t invertRGB565(uint16_t color) {
    // 分离颜色分量
    uint8_t r = (color >> 11) & 0x1F; // 取出红色分量
    uint8_t g = (color >> 5) & 0x3F;  // 取出绿色分量
    uint8_t b = color & 0x1F;         // 取出蓝色分量

    // 取反颜色分量
    r = 31 - r;
    g = 63 - g;
    b = 31 - b;

    // 重新组合颜色分量
    return (r << 11) | (g << 5) | b;
}

void TFT_ShowBar(TFT_T *dev, uint16_t x, uint16_t y, uint16_t width, uint16_t height, float progress) {
    if (x + width > dev->width || y + height > dev->height || progress > 1)return;
    uint16_t xp = (width - x) * progress;
    TFT_Fill(dev, x, y, xp, height, POINT_COLOR);
    if (progress < 1)TFT_Fill(dev, xp, y, width, height, BACK_COLOR);
    if (height >= 12 && height < 32) {
        uint8_t tmp[7];
        uint16_t sp = (width - x) / 2 - 3 * ((height >= 24) ? 24 : (height >= 16) ? 16 : 12) / 2;
        TFT_Color_t temp = POINT_COLOR;
        POINT_COLOR.u16 = invertRGB565(POINT_COLOR.u16);
        sprintf((char *) tmp, "%05.2f%%", progress * 100);
        if (xp < sp)TFT_ShowString(dev, sp, y, tmp, (height >= 24) ? 24 : (height >= 16) ? 16 : 12, 0);
        else TFT_ShowString(dev, sp, y, tmp, (height >= 24) ? 24 : (height >= 16) ? 16 : 12, 1);
        POINT_COLOR = temp;
    }
}
