#include "oled.h"
#include "ascii_font.h"

#ifdef HZK_FONT

#include "font_chuc.h"

#endif

#define BUFPOINT(x, y) (*(dev->buf + x + (y * dev->width)))
#define GET_BIT(x, bit)    ((x & (1 << bit)) >> bit)

const uint8_t initCmd[] = {
        0xAE,                // 关闭显示
        0xD5, 0x80,      // 设置显示时钟分频比/振荡器频率
        0xA8, 0x3F,      // 设置多路复用比率
        0xD3, 0x00,     // 设置显示偏移
        0x40, 0x00,   // 设置显示起始行
        0x8D, 0x14,   // 电荷泵设置
        0x20, 0x00,   // 设置内存寻址模式
        0xA0,              // 设置段重映射
        0xC8,              // 设置COM输出扫描方向
        0xDA, 0x12,   // 设置COM引脚硬件配置
        0x81, 0xCF,   // 设置对比度控制
        0xD9, 0xF1,   // 设置预充电周期
        0xDB, 0x20,   // 设置VCOMH取消电平
        0xA4, 0xA6,   // 整个显示打开
        0xAF               // 打开显示
};

void OLED_Init(OLED_T *dev) {
    uint8_t *cmdIndex = (uint8_t *) initCmd;
//    uint8_t count, temp;
//    while (*cmdIndex) {
//        temp = *cmdIndex++;
//        count = temp & 0x7F;
//
//        dev->cmd(cmdIndex, count);
//        cmdIndex += count;
//    }
    dev->cmd(cmdIndex, sizeof(initCmd));
    dev->state = IDLE;
}
void OLED_Init_CMD(OLED_T *dev,uint8_t *cmd,uint16_t len)
{
    dev->cmd(cmd, len);
    dev->state = IDLE;
}
void OLED_ON(OLED_T *dev) {
    uint8_t cmd[3] = {0x8D, 0x14, 0xAF};
    if (dev->state == IDLE) {
        dev->state = WRITE;
        dev->cmd(cmd, 3);
        dev->state = IDLE;
    }
}

void OLED_OFF(OLED_T *dev) {
    uint8_t cmd[3] = {0x8D, 0x10, 0xAE};
    if (dev->state == IDLE) {
        dev->state = WRITE;
        dev->cmd(cmd, 3);
        dev->state = IDLE;
    }
}

void OLED_Turn(OLED_T *dev, bool e) {
    uint8_t cmd = 0xA6 + e;
    if (dev->state == IDLE) {
        dev->state = WRITE;
        dev->cmd(&cmd, 1);
        dev->state = IDLE;
    }
}

void OLED_Refresh(OLED_T *dev) {
#if REFRESH_CALL_ENABLE
    dev->call(dev);
#else
    uint8_t i, cmd[3] = {0xb0, 0x00, 0x10};
    if (dev->state == IDLE) {
        dev->state = REFRESH;
        for (i = 0; i < (dev->height >> 3); i++) {
            cmd[0] = 0xb0 + i;
            dev->cmd(cmd, 3);
            dev->data(dev->buf + (i * dev->width), dev->width);
        }
        dev->state = IDLE;
    }
#endif
}

void OLED_DisplayTurn(OLED_T *dev, bool e) {
    uint8_t cmd[2];
    if (e) {
        cmd[0] = 0xC8;
        cmd[1] = 0xA1;

    } else {
        cmd[0] = 0xC0;
        cmd[1] = 0xA0;
    }
    if (dev->state == IDLE) {
        dev->state = WRITE;
        dev->cmd(cmd, 1);
        dev->state = IDLE;
    }
}

void OLED_Set(OLED_T *dev, uint8_t x, uint8_t y) {
    uint8_t i, m, n;
    i = y / 8;
    m = y % 8;
    n = 1 << m;
    BUFPOINT(x, i) |= n;
}

// 列写入
void OLED_BSet(OLED_T *dev, uint8_t x, uint8_t y, uint8_t data) {
    uint8_t i, ys = y;
    for (i = 0; i < 8; i++)           //写入数据
    {
        if (GET_BIT(data, i))OLED_Set(dev, x, ys);
        else OLED_RSet(dev, x, ys);
        ys++;
    }
}

// 行写入
void OLED_HBSet(OLED_T *dev, uint8_t x, uint8_t y, uint8_t data) {
    uint8_t i, ys = y;
    x += 7;
    for (i = 0; i < 8; i++) // 写入数据
    {
        if (GET_BIT(data, i))
            OLED_Set(dev, x, ys);
        else
            OLED_RSet(dev, x, ys);
        x--;
    }
}

void OLED_RSet(OLED_T *dev, uint8_t x, uint8_t y) {
    uint8_t i, m, n;
    i = y / 8;
    m = y % 8;
    n = 1 << m;
    BUFPOINT(x, i) = ~BUFPOINT(x, i);
    BUFPOINT(x, i) |= n;
    BUFPOINT(x, i) = ~BUFPOINT(x, i);
}

void OLED_DrawLine(OLED_T *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    uint8_t i, k, k1, k2;
    if ((x1 < 0) || (x2 > dev->width) || (y1 < 0) || (y2 > dev->height) || (x1 > x2) || (y1 > y2))return;
    if (x1 == x2)    //画竖线
    {
        for (i = 0; i < (y2 - y1); i++) {
            OLED_Set(dev, x1, y1 + i);
        }
    } else if (y1 == y2)   //画横线
    {
        for (i = 0; i < (x2 - x1); i++) {
            OLED_Set(dev, x1 + i, y1);
        }
    } else      //画斜线
    {
        k1 = y2 - y1;
        k2 = x2 - x1;
        k = k1 * 10 / k2;
        for (i = 0; i < (x2 - x1); i++) {
            OLED_Set(dev, x1 + i, y1 + i * k / 10);
        }
    }
}

void OLED_DrawRect(OLED_T *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    OLED_DrawLine(dev, x1, y1, x2, y1); // Top side
    OLED_DrawLine(dev, x1, y1, x1, y2); // Left side
    OLED_DrawLine(dev, x2, y1, x2, y2); // Right side
    OLED_DrawLine(dev, x1, y2, x2, y2); // Bottom side
}

void OLED_DrawCircle(OLED_T *dev, uint8_t x, uint8_t y, uint8_t r) {
    int a, b, num;
    a = 0;
    b = r;
    while (2 * b * b >= r * r) {
        OLED_Set(dev, x + a, y - b);
        OLED_Set(dev, x - a, y - b);
        OLED_Set(dev, x - a, y + b);
        OLED_Set(dev, x + a, y + b);

        OLED_Set(dev, x + b, y + a);
        OLED_Set(dev, x + b, y - a);
        OLED_Set(dev, x - b, y - a);
        OLED_Set(dev, x - b, y + a);

        a++;
        num = (a * a + b * b) - r * r;//计算画的点离圆心的距离
        if (num > 0) {
            b--;
            a--;
        }
    }
}

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

void OLED_ShowString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str, uint8_t size1) {
    while ((*str >= ' ') && (*str <= '~'))//判断是不是非法字符!
    {
        OLED_ShowChar(dev, x, y, *str, size1);
        x += size1 / 2;
        if (x > dev->width - size1)  //换行
        {
            x = 0;
            y += 2;
        }
        str++;
    }
}

#ifdef HZK_FONT

void OLED_ShowCHChr(OLED_T *dev, uint8_t x, uint8_t y, Chinese_t *hzk) {
    uint8_t i, temp, size2 = Hzk_size * ((Hzk_size + 7) / 8 * 8) / 8;
    uint16_t ziku_byte_n, ziku_bytes = ((Hzk_size + 7) / 8 * 8) / 8;
    ziku_byte_n = 0;
    for (i = 0; i < size2; i++) {
        temp = hzk->data[i];
        OLED_HBSet(dev, x + ziku_byte_n * 8, y, temp);
        ziku_byte_n++;
        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 OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str) {
    uint32_t unicode_letter;
    uint8_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){
                OLED_ShowCHChr(dev, x, y, hzk);
                x += Hzk_size;
                str += step;
                found = true;
            }
        }
        if (!found) {
            OLED_ShowChar(dev, x, y, *str, Hzk_size);
            x += Hzk_size / 2;
            str++;
        }
        if (x > dev->width - Hzk_size) {
            x = 0;
            y += Hzk_size;
        }
    }
}

#else
void OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str) {
    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]) // 对比汉字区码位码
            {
                OLED_ShowCHChr(dev, x, y, &Hzk[index]);
                x += Hzk_size;
                str++;
                str++;
                found = true;
                break;
            }
        }
        if (!found) {
            OLED_ShowChar(dev, x, y, *str, Hzk_size);
            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, uint8_t *data, size_t len) {
    size_t index;
    uint16_t x = fd->x, y = fd->y;
    uint8_t bit;
    for (index = 0; index < len; index++) {
        bit = (data[index / 8] >> (index % 8)) & 1;
        if (bit)OLED_Set((OLED_T *) fd->dev, x, y);
        else OLED_RSet((OLED_T *) fd->dev, x, y);
        x++;
        if ((x - fd->x) == fd->w) {
            y++;
            x = fd->x;
        }
    }
}

void OLED_DisplayString(OLED_T *dev, const lv_font_t *font, uint8_t *s, uint16_t x, uint16_t y) {
    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 {
            OLED_ShowChar(dev, x, y, *s, 12);
            x += 12;
            s++;
        }
    }
}
#endif

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

void OLED_ShowNum(OLED_T *dev, uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size1) {
    uint8_t t, temp;
    for (t = 0; t < len; t++) {
        temp = (num / OLED_Pow(10, len - t - 1)) % 10;
        if (temp == 0) {
            OLED_ShowChar(dev, x + (size1 / 2) * t, y, '0', size1);
        } else {
            OLED_ShowChar(dev, x + (size1 / 2) * t, y, temp + '0', size1);
        }
    }
}

void OLED_ShowPic(OLED_T *dev, uint8_t x0, uint8_t y0, uint8_t w, uint8_t h, uint8_t *bmp) {
    size_t index;
    uint16_t x = x0, y = y0;
    for (index = 0; index < (w * h) / 8; index++) {
        OLED_BSet(dev, x, y, bmp[index]);
        x++;
        if ((x - x0) == w) {
            y += 8;
            x = x0;
        }
    }
}

void OLED_Fill(OLED_T *dev, uint8_t data) {
    uint8_t x, y;
    for (y = 0; y < (dev->height >> 3); y++) {
        for (x = 0; x < dev->width; x++) {
            BUFPOINT(x, y) = data;
        }
    }
}

void OLED_CLS(OLED_T *dev) {
    OLED_Fill(dev, 0x00);
}

void OLED_SPos(OLED_T *dev, uint8_t x, uint8_t y) {
    uint8_t cmd[3];
    cmd[0] = 0xb0 + y;
    cmd[1] = ((x & 0xf0) >> 4) | 0x10;
    cmd[2] = (x & 0x0f) | 0x01;
    if (dev->state == IDLE) {
        dev->state = WRITE;
        dev->cmd(cmd, 3);
        dev->state = IDLE;
    }
}