//游戏引擎文字显示库

#include "stdio.h"

#include "sys.h"
#include "lcd.h"
#include "GameEngine.h"

#include "GE_Font.h"

/**************************************** 私有定义 ****************************************/

#define GE_Font_GetACSIIDataSize(__font_size__) ((__font_size__ / 8 + ((__font_size__ % 8) ? 1 : 0)) * (__font_size__ / 2))
#define GE_Font_GetGBKDataSize(__font_size__) ((__font_size__ / 8 + ((__font_size__ % 8) ? 1 : 0)) * (__font_size__))

/*****************************************************************************************/

/**************************************** 私有函数 ****************************************/

uint8_t GE_Font_GetACSII(uint8_t *font, uint8_t *ch, uint8_t font_size);
uint8_t GE_Font_PrintACSII(uint16_t x, uint16_t y, uint8_t *ch, uint8_t font_size, uint16_t font_color, uint16_t back_color, uint8_t is_transparent);

uint8_t GE_Font_GetGBK(uint8_t *font, uint8_t *ch, uint8_t font_size);
uint8_t GE_Font_PrintGBK(uint16_t x, uint16_t y, uint8_t *ch, uint8_t font_size, uint16_t font_color, uint16_t back_color, uint8_t is_transparent);

/*****************************************************************************************/

_ge_font_print_set ge_font_print_set;

/**
  * @brief  初始化 GE_Font
  */
void GE_Font_Init(void)
{
    //绘制汉字设置
    ge_font_print_set.font_size = FONT_12;
    ge_font_print_set.font_color = BLACK;
    ge_font_print_set.back_color = WHITE;
    ge_font_print_set.is_transparent = TRUE;
}

/**
  * @brief  从字库读 ACSII 字符。说明略
  */
uint8_t GE_Font_GetACSII(uint8_t *font, uint8_t *ch, uint8_t font_size)
{
    uint32_t addr, offset;
    uint16_t data_size = GE_Font_GetACSIIDataSize(font_size);

    offset = (uint32_t)data_size * (*ch);

    switch (font_size)
    {
    case FONT_12:
        addr = offset + ADDR_ASCII12;
        break;
    case FONT_16:
        addr = offset + ADDR_ASCII16;
        break;
    case FONT_24:
        addr = offset + ADDR_ASCII24;
        break;
    case FONT_32:
        addr = offset + ADDR_ASCII32;
        break;
    case FONT_48:
        addr = offset + ADDR_ASCII48;
        break;
    case FONT_64:
        addr = offset + ADDR_ASCII64;
        break;

    default:
        return 1;
    }

    LCD_Font_ReadAddr(font, addr, data_size); //从字库读取字符到 font

    return 0;
}

/**
  * @brief  显示 ACSII 字符。说明略
  */
uint8_t GE_Font_PrintACSII(uint16_t x, uint16_t y, uint8_t *ch, uint8_t font_size, uint16_t font_color, uint16_t back_color, uint8_t is_transparent)
{
    uint16_t data_size = GE_Font_GetACSIIDataSize(font_size);

    //从字库获取字符
    uint8_t font[256]; //使用 FONT_64 时需要 256 大小
    if (GE_Font_GetACSII(font, ch, font_size) != 0)
        return 1;

    uint8_t temp8;
    uint16_t x0 = x, y0 = y;

    //循环纵向输出
    for (uint16_t i = 0; i < data_size; i++)
    {
        temp8 = font[i];

        for (uint16_t j = 0; j < 8; j++)
        {
            if (temp8 & 0x80)
                GE_Draw_Point(x, y, font_color);
            else if (is_transparent == FALSE) //不透明时使用背景色填充
                GE_Draw_Point(x, y, back_color);

            temp8 <<= 1;

            y++;
            if ((y - y0) == font_size)
            {
                y = y0;
                x++;
                break;
            }
        }
    }

    return 0;
}

/**
  * @brief  从字库读 GBK 字符。说明略
  */
uint8_t GE_Font_GetGBK(uint8_t *font, uint8_t *ch, uint8_t font_size)
{
    if (font_size >= FONT_48)
        return 1;

    uint32_t addr, offset;
    uint16_t data_size = GE_Font_GetGBKDataSize(font_size);

    uint8_t qh = *ch;
    uint8_t ql = *(++ch);

    if (qh < 0x81 || ql < 0x40 || ql == 0xff || qh == 0xff) //非法访问
        return 1;

    if (ql < 0x7f)
        ql -= 0x40;
    else
        ql -= 0x41;

    qh -= 0x81;

    offset = (uint32_t)data_size * (190 * (uint32_t)qh + ql);

    switch (font_size)
    {
    case FONT_12:
        addr = offset + ADDR_GBK12;
        break;
    case FONT_16:
        addr = offset + ADDR_GBK16;
        break;
    case FONT_24:
        addr = offset + ADDR_GBK24;
        break;
    case FONT_32:
        addr = offset + ADDR_GBK32;
        break;

    default:
        return 1;
    }

    LCD_Font_ReadAddr(font, addr, data_size); //从字库读取字符到 font

    return 0;
}

/**
  * @brief  显示 GBK 字符。说明略
  */
uint8_t GE_Font_PrintGBK(uint16_t x, uint16_t y, uint8_t *ch, uint8_t font_size, uint16_t font_color, uint16_t back_color, uint8_t is_transparent)
{
    uint16_t data_size = GE_Font_GetGBKDataSize(font_size);

    //从字库获取字符
    uint8_t font[128]; //使用 FONT_32 时需要 128 大小
    if (GE_Font_GetGBK(font, ch, font_size) != 0)
        return 1;

    uint8_t temp8;
    uint16_t x0 = x, y0 = y;

    //循环纵向输出
    for (uint16_t i = 0; i < data_size; i++)
    {
        temp8 = font[i];

        for (uint16_t j = 0; j < 8; j++)
        {
            if (temp8 & 0x80)
                GE_Draw_Point(x, y, font_color);
            else if (is_transparent == FALSE) //不透明时使用背景色填充
                GE_Draw_Point(x, y, back_color);

            temp8 <<= 1;

            y++;
            if ((y - y0) == font_size)
            {
                y = y0;
                x++;
                break;
            }
        }
    }

    return 0;
}

/**
  * @brief  在指定区域内显示字符串。支持 ASCII、GBK 汉字
  * @param  x_start
  * @param  y_start
  * @param  width: 显示窗的宽
  * @param  height: 显示窗口的高
  * @param  font_size: 字体大小。注意：若含汉字，只能使用 FONT_12 16 24 32
  * @param  font_color: 字体颜色
  * @param  back_color: 背景颜色。透明时无效
  * @param  is_transparent: 是否透明
  * @param  format: 格式字符串
  * @param  arg: 参数表
  * @retval 打印完全返回 1，未打印完全返回0 
  */
uint8_t GE_Font_Print_Va(
    int16_t x_start,
    int16_t y_start,
    uint16_t width,
    uint16_t height,
    uint8_t font_size,
    uint16_t font_color,
    uint16_t back_color,
    uint8_t is_transparent,
    uint8_t *format,
    va_list arg)
{
    int16_t x_end_plus_1;
    int16_t y_end_plus_1;
    int16_t x = x_start;
    int16_t y = y_start;

    if (width == BORDER_MAX)
        x_end_plus_1 = LCD_WIDTH;
    else
        x_end_plus_1 = x_start + width;

    if (height == BORDER_MAX)
        y_end_plus_1 = LCD_HEIGHT;
    else
        y_end_plus_1 = y_start + height;

    uint8_t is_print_all = FALSE;

    uint8_t temp_str[1040];
    uint8_t *str = temp_str;

    vsprintf(str, format, arg);

    while (1)
    {
        if (*(str) <= 0x80) //ASCII 字符
        {
            if (*str == '\0')
            {
                is_print_all = TRUE;
                break;
            }

            if (*str == '\r')
            {
                str++;
                continue;
            }

            if ((x + font_size / 2 > x_end_plus_1) || (*str == '\n'))
            {
                x = x_start;
                y += font_size;

                if (y + font_size > y_end_plus_1)
                    break;
            }

            if (*str != '\n')
            {
                GE_Font_PrintACSII(x, y, str, font_size, font_color, back_color, is_transparent);
                x += font_size / 2;
            }

            str++;
        }
        else if (*(str) > 0x80) //GBK 字符
        {
            if (x + font_size > x_end_plus_1)
            {
                x = x_start;
                y += font_size;

                if (y + font_size > y_end_plus_1)
                    break;
            }

            GE_Font_PrintGBK(x, y, str, font_size, font_color, back_color, is_transparent);
            x += font_size;
            str++;
            str++;
        }
    }

    return is_print_all;
}

/**
  * @brief  在指定区域内显示字符串。支持 ASCII、GBK 汉字
  * @param  x_start
  * @param  y_start
  * @param  width: 显示窗的宽
  * @param  height: 显示窗口的高
  * @param  font_size: 字体大小。注意：若含汉字，只能使用 FONT_12 16 24 32
  * @param  font_color: 字体颜色
  * @param  back_color: 背景颜色。透明时无效
  * @param  is_transparent: 是否透明
  * @param  format: 格式字符串。用法与 printf 相同
  * @param  ...
  * @retval 打印完全返回 1，未打印完全返回0 
  */
uint8_t GE_Font_Print(
    int16_t x_start,
    int16_t y_start,
    uint16_t width,
    uint16_t height,
    uint8_t font_size,
    uint16_t font_color,
    uint16_t back_color,
    uint8_t is_transparent,
    uint8_t *format,
    ...)
{
    va_list aptr;
    va_start(aptr, format);

    uint8_t ret = GE_Font_Print_Va(
        x_start,
        y_start,
        width,
        height,
        font_size,
        font_color,
        back_color,
        is_transparent,
        format,
        aptr);

    va_end(aptr);
    return ret;
}

/**
  * @brief  在指定区域内显示字符串，使用设置。支持 ASCII、GBK 汉字
  * @param  x_start
  * @param  y_start
  * @param  width: 显示窗的宽
  * @param  height: 显示窗口的高
  * @param  format: 格式字符串
  * @param  arg: 参数表
  * @retval 打印完全返回 1，未打印完全返回0 
  */
uint8_t GE_Font_Print_WithSet_Va(
    int16_t x_start,
    int16_t y_start,
    uint16_t width,
    uint16_t height,
    uint8_t *format,
    va_list arg)
{
    return GE_Font_Print_Va(
        x_start,
        y_start,
        width,
        height,
        ge_font_print_set.font_size,
        ge_font_print_set.font_color,
        ge_font_print_set.back_color,
        ge_font_print_set.is_transparent,
        format,
        arg);
}

/**
  * @brief  在指定区域内显示字符串，使用设置。支持 ASCII、GBK 汉字
  * @param  x_start
  * @param  y_start
  * @param  width: 显示窗的宽
  * @param  height: 显示窗口的高
  * @param  format: 格式字符串。用法与 printf 相同
  * @param  ...
  * @retval 打印完全返回 1，未打印完全返回0 
  */
uint8_t GE_Font_Print_WithSet(
    int16_t x_start,
    int16_t y_start,
    uint16_t width,
    uint16_t height,
    uint8_t *format,
    ...)
{
    va_list aptr;
    va_start(aptr, format);

    uint8_t ret = GE_Font_Print_WithSet_Va(
        x_start,
        y_start,
        width,
        height,
        format,
        aptr);

    va_end(aptr);
    return ret;
}
