#include "renderer_lcd.h"

#include "lcd.h"
#include "lcd_draw.h"
#include "lcd_text.h"

/* MSVC 对含多字节/中文格式串的静态分析可能误报 C4474/C4996。
 * 本文件内的格式化输出均为受控 buffer，并用于显示文本渲染。 */
 #if defined(_MSC_VER)
 #pragma warning(disable : 4474)
 #pragma warning(disable : 4996)
 #endif

static uint16_t PageRenderer_LcdSizeX(void)
{
    return LCD_SIZE_X;
}

static uint16_t PageRenderer_LcdSizeY(void)
{
    return LCD_SIZE_Y;
}

static uint8_t PageRenderer_LcdColorFont(void)
{
    return LCD_FONT;
}

static uint8_t PageRenderer_LcdColorBack(void)
{
    return LCD_BACK;
}
static uint16_t PageRenderer_LcdGetASCIIWidth(void)
{
    return text_cfg.wASCIIFontWidth;
}
static uint16_t PageRenderer_LcdGetASCIIHeight(void)
{
    return text_cfg.wASCIIFontHeight;
}
/* -------------------------------------------------------------------------
 * 函数名: PageRenderer_LcdGetUtf8Len
 * 功能:
 *   计算 UTF-8 字符串在当前菜单显示规则下的“显示宽度”。
 *
 * 参数:
 *   str - 待计算的 UTF-8 字符串
 *
 * 边界处理:
 *   - 本函数不做空指针校验，调用方需保证 `str` 非空且为 `\0` 结尾。
 *   - 依赖 `utf8_next()` 逐字符解析 UTF-8 编码；若输入非法，结果由底层解析行为决定。
 *
 * 说明:
 *   - 当前显示规则约定：
 *       1) ASCII/单字节字符宽度记为 1
 *       2) 多字节 UTF-8 字符（如中文）宽度记为 2
 *   - 本函数返回的是“显示占位宽度”，不是原始字节长度。
 *   - 该结果会被菜单布局函数用于计算菜单框宽度与字符串显示长度。
 *
 * 返回值:
 *   - UTF-8 字符串的显示宽度
 * ------------------------------------------------------------------------- */
 static uint16_t PageRenderer_LcdGetUtf8Len(const uint8_t *str)
 {
     uint16_t strLen = 0;
     uint32_t unicode;
     uint8_t index = 0;
     uint8_t n = 0;
 
     while (str[index] != '\0')
     {
         n = utf8_next(str + index, &unicode);
         strLen += (n > 1) ? 2 : 1;
         index += n;
     }
     return strLen;
}
static uint16_t PageRenderer_LcdGetRowSpace(void)
{
    return text_cfg.rowSpace;
}
static void PageRenderer_ClearScreen(void)
{
    Lcd_FillRect(0, 0, LCD_SIZE_X - 1, LCD_SIZE_Y - 1, LCD_BACK);
}
/* -------------------------------------------------------------------------
* 函数名: PageRenderer_LcdDrawMeitou
* 功能:
*   绘制菜单标题装饰线（“眉头”样式）：中间横线 + 左右两端斜线。
*
* 参数:
*   yStart - 装饰线基准 Y 坐标（横线所在行）
*   width  - 线宽参数（传递给 line_h/line）
*
* 边界处理:
*   - 坐标范围合法性由上层布局计算保证。
*
* 说明:
*   - 颜色统一使用字体前景色（PageRenderer_LcdColorFont），确保装饰与文字风格一致。
*   - 绘制顺序：
*       1) 中间横线（x:16~144）
*       2) 左斜线（8, yStart-8 -> 16, yStart）
*       3) 右斜线（144, yStart -> 152, yStart-8）
*   - 该函数用于视觉分隔与层次强调，不改变菜单状态数据。
*
* 返回值:
*   - 无
* ------------------------------------------------------------------------- */
static void PageRenderer_LcdDrawMeitou(uint16_t yStart, uint16_t width)
{
      uint8_t fontColor = PageRenderer_LcdColorFont();
      Lcd_LineH(16, 144, yStart, width, fontColor);
      Lcd_Line(8, yStart - 8, 16, yStart, width, fontColor);
      Lcd_Line(144, yStart, 152, yStart - 8, width, fontColor);
}

void PageRenderer_LcdShowInfoPage(uint16_t wPageNum, uint16_t wPageMax )
{
    /* show_str 期望的是 '\0' 结尾的 UTF-8 字符串 */
    uint8_t pbyTips[64];
    uint16_t y = LCD_SIZE_Y - 16;

    /* 格式化字符串（用于 LCD 文本显示） */
#if defined(_MSC_VER)
    (void)sprintf_s((char *)pbyTips, sizeof(pbyTips), "第%4u 页 , 共%4u 页",
                     (unsigned)wPageNum, (unsigned)wPageMax);
#else
    (void)snprintf((char *)pbyTips, sizeof(pbyTips), "第%4u 页 , 共%4u 页",
                    (unsigned)wPageNum, (unsigned)wPageMax);
#endif

    Lcd_ShowStr(6, y, pbyTips);
}

static const PageRenderPort g_lcd_port = {
    .get_size_x = PageRenderer_LcdSizeX,
    .get_size_y = PageRenderer_LcdSizeY,
    .get_ascii_width = PageRenderer_LcdGetASCIIWidth,
    .get_ascii_height = PageRenderer_LcdGetASCIIHeight,
    .get_color_font = PageRenderer_LcdColorFont,
    .get_color_back = PageRenderer_LcdColorBack,
    .get_utf8_len = PageRenderer_LcdGetUtf8Len,
    .get_row_space = PageRenderer_LcdGetRowSpace,
    .fill_rect = Lcd_FillRect,
    .line_h = Lcd_LineH,
    .line_v = Lcd_LineV,
    .line = Lcd_Line,
    .set_pixel = Lcd_SetPixel,
    .invert = Lcd_Invert,
    .show_str = Lcd_ShowStr,
    .clear_screen = PageRenderer_ClearScreen,
    .draw_meitou = PageRenderer_LcdDrawMeitou,
    .show_info_page = PageRenderer_LcdShowInfoPage,
};

const PageRenderPort *PageRenderer_Lcd(void)
{
    return &g_lcd_port;
}
