/* -------------------------------------------------------------------------
 * 文件名: test_p1_lcd_basic.c
 * 作用:
 *   对 LCD 基础模块进行单元测试，覆盖以下三个子模块：
 *   1) lcd.c       : 显存初始化、像素读写、边界保护
 *   2) lcd_draw.c  : 填充、反显、水平线/垂直线绘制
 *   3) lcd_text.c  : UTF-8 解码、字符串显示（ASCII/中文/换行）与边界返回值
 *
 * 测试策略:
 *   - 每个 test_* 函数仅验证一个明确行为域，失败即立即返回非 0。
 *   - 使用 ASSERT_* 宏统一断言风格，失败时打印文件+行号。
 *   - main() 串行执行所有子用例，便于定位首个失败点。
 *
 * 设计说明:
 *   - 本文件只依赖公开头文件，不包含被测 .c，实现黑盒+灰盒结合验证。
 *   - 通过“像素点结果”验证绘图函数，不依赖外设或真实 LCD 硬件。
 * ------------------------------------------------------------------------- */
#include "../src/Drv/lcd/lcd.h"
#include "../src/Drv/lcd/lcd_draw.h"
#include "../src/Drv/lcd/lcd_text.h"
#include "test_common.h"

/* -------------------------------------------------------------------------
 * 用例: test_lcd_init_and_pixel_rw
 * 目标:
 *   - 验证 Lcd_Init() 会把显存清零（黑色）
 *   - 验证 Lcd_SetPixel/Lcd_GetPixel 的基本读写正确性
 *   - 验证越界写入/读取均返回错误码
 *
 * 覆盖函数:
 *   - Lcd_Init
 *   - Lcd_SetPixel
 *   - Lcd_GetPixel
 * ------------------------------------------------------------------------- */
static int test_lcd_init_and_pixel_rw(void)
{
    /* 初始化后显存应为全黑（0x00） */
    Lcd_Init();
    /* 左上角像素校验 */
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(0, 0));
    /* 右下角像素校验，确认全屏范围都被正确初始化 */
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(LCD_SIZE_X - 1, LCD_SIZE_Y - 1));

    /* 在合法坐标写白点并回读 */
    ASSERT_EQ_INT(LCD_OK, Lcd_SetPixel(1, 1, LCD_COLOR_WHITE));
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(1, 1));

    /* 非法颜色应返回错误码，且不改变原有像素值 */
    ASSERT_EQ_INT(LCD_ERR_INVALID_COLOR, Lcd_SetPixel(1, 1, 0x7F));
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(1, 1));

    /* 越界写入应返回错误码：x == LCD_SIZE_X 已越界 */
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_SetPixel(LCD_SIZE_X, 1, LCD_COLOR_WHITE));
    /* 越界写入应返回错误码：y == LCD_SIZE_Y 已越界 */
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_SetPixel(1, LCD_SIZE_Y, LCD_COLOR_WHITE));
    /* 越界读取应返回错误码 */
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_GetPixel(LCD_SIZE_X, 1));
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_GetPixel(1, LCD_SIZE_Y));
    
    /* 附加点位校验，确保越界写没有污染邻近合法区域 */
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(2, 2));
    return 0;
}

/* -------------------------------------------------------------------------
 * 用例: test_fillrect_and_invert
 * 目标:
 *   - 验证 Lcd_FillRect 的闭区间填充行为（包含 right_x/bottom_y）与错误码
 *   - 验证 Lcd_Invert 的半开区间行为（[min, max)）与错误码
 *   - 验证 Lcd_Invert 支持反向坐标输入（起点大于终点）
 *
 * 覆盖函数:
 *   - Lcd_FillRect
 *   - Lcd_Invert
 * ------------------------------------------------------------------------- */
static int test_fillrect_and_invert(void)
{
    Lcd_Init();

    /* 填充 3x3 白色区域：x=[2,4], y=[2,4]（闭区间） */
    ASSERT_EQ_INT(LCD_OK, Lcd_FillRect(2, 2, 4, 4, LCD_COLOR_WHITE));
    /* 左上角在填充区内 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(2, 2));
    /* 右下角也在填充区内，验证 FillRect 含边界 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(4, 4));
    /* 区域外点保持黑色 */
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(5, 5));

    /* Invert 采用 [min, max) 半开区间：会翻转 x=2..3, y=2..3 */
    ASSERT_EQ_INT(LCD_OK, Lcd_Invert(2, 2, 4, 4));
    /* 区域内像素从白翻转成 ~白 */
    ASSERT_EQ_INT((uint8_t)~LCD_COLOR_WHITE, Lcd_GetPixel(2, 2));
    ASSERT_EQ_INT((uint8_t)~LCD_COLOR_WHITE, Lcd_GetPixel(3, 3));
    /* x=4/y=4 在半开区间外，不应被翻转 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(4, 4));

    /* 反向坐标也应可处理：再次翻转回白色 */
    ASSERT_EQ_INT(LCD_OK, Lcd_Invert(4, 4, 2, 2));
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(2, 2));
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(3, 3));

    /* 越界参数应返回错误码 */
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_FillRect(0, 0, LCD_SIZE_X, 4, LCD_COLOR_WHITE));
    ASSERT_EQ_INT(LCD_ERR_INVALID_COLOR, Lcd_FillRect(0, 0, 1, 1, 0x7F));
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_Invert(0, 0, LCD_SIZE_X + 1, 4));

    return 0;
}

/* -------------------------------------------------------------------------
 * 用例: test_lineh_linev
 * 目标:
 *   - 验证 Lcd_LineH/Lcd_LineV 的绘制范围与线宽、错误码
 *   - 验证水平/垂直线的“结束坐标不包含”语义
 *   - 验证 Lcd_LineH 的反向坐标能力
 *
 * 覆盖函数:
 *   - Lcd_LineH
 *   - Lcd_LineV
 * ------------------------------------------------------------------------- */
static int test_lineh_linev(void)
{
    Lcd_Init();

    /* 水平线：x=[10,14), y=[20,22)；线宽=2 */
    ASSERT_EQ_INT(LCD_OK, Lcd_LineH(10, 14, 20, 2, LCD_COLOR_WHITE));
    /* 起点应该被绘制 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(10, 20));
    /* 范围内末端附近点（x=13,y=21）应为白 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(13, 21));
    /* xEnd 不包含：x=14 不应被画 */
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(14, 20)); /* xEnd 不包含 */

    /* 垂直线：x=[40,42), y=[30,35)；线宽=2 */
    ASSERT_EQ_INT(LCD_OK, Lcd_LineV(30, 35, 40, 2, LCD_COLOR_WHITE));
    /* 起点应被绘制 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(40, 30));
    /* 范围内末端附近点应为白 */
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(41, 34));
    /* yEnd 不包含：y=35 不应被画 */
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(40, 35)); /* yEnd 不包含 */

    /* 水平线支持反向 X：函数内部应自动交换起止坐标 */
    ASSERT_EQ_INT(LCD_OK, Lcd_LineH(14, 10, 50, 1, LCD_COLOR_WHITE));
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(10, 50));
    ASSERT_EQ_INT(LCD_COLOR_WHITE, Lcd_GetPixel(13, 50));
    ASSERT_EQ_INT(LCD_COLOR_BLACK, Lcd_GetPixel(14, 50));

    ASSERT_EQ_INT(LCD_ERR_INVALID_COLOR, Lcd_LineH(10, 14, 20, 1, 0x7F));
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_LineH(10, 14, LCD_SIZE_Y, 1, LCD_COLOR_WHITE));
    ASSERT_EQ_INT(LCD_ERR_OUT_OF_RANGE, Lcd_LineV(10, 14, LCD_SIZE_X, 1, LCD_COLOR_WHITE));

    return 0;
}

/* -------------------------------------------------------------------------
 * 工具函数: count_white_in_rect
 * 作用:
 *   统计指定闭区间矩形内白色像素数量。
 * 用途:
 *   文本渲染（尤其字形）不便逐像素写期望值时，用“白点数 > 0”验证
 *   “确实有内容被渲染”这一最小正确性。
 *
 * 参数:
 *   x0,y0 : 左上角（包含）
 *   x1,y1 : 右下角（包含）
 *
 * 返回:
 *   区域内等于 LCD_COLOR_WHITE 的像素个数。
 * ------------------------------------------------------------------------- */
static int count_white_in_rect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    int count = 0;
    for (uint16_t y = y0; y <= y1; y++) {
        for (uint16_t x = x0; x <= x1; x++) {
            if (Lcd_GetPixel(x, y) == LCD_COLOR_WHITE) {
                count++;
            }
        }
    }
    return count;
}

/* -------------------------------------------------------------------------
 * 用例: test_utf8_next_decode
 * 目标:
 *   - 覆盖 utf8_next 对 1/2/3 字节 UTF-8 的解码路径
 *   - 验证不支持或非法输入时返回 0
 *
 * 覆盖函数:
 *   - utf8_next
 * ------------------------------------------------------------------------- */
static int test_utf8_next_decode(void)
{
    uint32_t unicode = 0;
    const unsigned char ascii[] = "A";                   /* 单字节 ASCII */
    const unsigned char u2[] = {0xC2, 0xA2, 0x00};       /* 两字节: U+00A2 */
    const unsigned char u3[] = {0xE4, 0xBD, 0xA0, 0x00}; /* 三字节: U+4F60 */
    const unsigned char invalid[] = {0xF0, 0x00};        /* 四字节起始（当前实现不支持） */
    const unsigned char trunc2[] = {0xC2, 0x00};         /* 两字节截断 */
    const unsigned char trunc3[] = {0xE4, 0xBD, 0x00};   /* 三字节截断 */

    /* ASCII: 返回长度 1，码点 0x41 */
    ASSERT_EQ_INT(1, utf8_next(ascii, &unicode));
    ASSERT_EQ_U32(0x41u, unicode);

    /* 两字节 UTF-8 */
    ASSERT_EQ_INT(2, utf8_next(u2, &unicode));
    ASSERT_EQ_U32(0x00A2u, unicode);

    /* 三字节 UTF-8 */
    ASSERT_EQ_INT(3, utf8_next(u3, &unicode));
    ASSERT_EQ_U32(0x4F60u, unicode);

    /* 非支持输入返回 0 */
    ASSERT_EQ_INT(0, utf8_next(invalid, &unicode));
    ASSERT_EQ_INT(0, utf8_next(trunc2, &unicode));
    ASSERT_EQ_INT(0, utf8_next(trunc3, &unicode));
    return 0;
}

/* -------------------------------------------------------------------------
 * 用例: test_showstr_ascii_utf8_newline_and_bounds
 * 目标:
 *   - 验证 Lcd_ShowStr 对 ASCII/中文渲染均生效
 *   - 验证换行逻辑（行高 + 行间距）正确
 *   - 验证越界起始坐标时返回错误码
 *
 * 覆盖函数:
 *   - Lcd_ShowStr
 * ------------------------------------------------------------------------- */
static int test_showstr_ascii_utf8_newline_and_bounds(void)
{
    uint8_t text_a[] = "A";
    uint8_t text_cn[] = "你";
    uint8_t text_nl[] = "A\nB";
    uint8_t text_cn_overflow_x[] = "你你";
    uint8_t text_nl_overflow_y[] = "A\nB";

    /* ASCII 渲染: 返回 0 且字形区域应存在白点 */
    Lcd_Init();
    ASSERT_EQ_INT(0, Lcd_ShowStr(0, 0, text_a));
    ASSERT_TRUE(count_white_in_rect(0, 0, 6, 11) > 0);

    /* 中文渲染: 13x12 区域内应存在白点 */
    Lcd_Init();
    ASSERT_EQ_INT(0, Lcd_ShowStr(10, 10, text_cn));
    ASSERT_TRUE(count_white_in_rect(10, 10, 22, 21) > 0);

    /* 换行渲染: "A\nB" 中 B 应下移到 y = 12 + rowSpace(2) = 14 */
    Lcd_Init();
    ASSERT_EQ_INT(0, Lcd_ShowStr(0, 0, text_nl));
    ASSERT_TRUE(count_white_in_rect(0, 0, 6, 11) > 0);       /* A */
    ASSERT_TRUE(count_white_in_rect(0, 14, 6, 25) > 0);      /* B: 12 高 + 2 行距 */

    /* 起始越界：x 方向越界应返回 -1 */
    ASSERT_EQ_INT(-1, Lcd_ShowStr(LCD_SIZE_X - 6, 0, text_a));
    /* 起始越界：y 方向越界应返回 -2 */
    ASSERT_EQ_INT(-2, Lcd_ShowStr(0, LCD_SIZE_Y - 12, text_a));

    /* 中文第 2 个字符越过右边界时应返回 -1 */
    Lcd_Init();
    ASSERT_EQ_INT(-1, Lcd_ShowStr(LCD_SIZE_X - 13, 0, text_cn_overflow_x));

    /* 换行后若超出底部边界，应返回 -2 */
    Lcd_Init();
    ASSERT_EQ_INT(-2, Lcd_ShowStr(0, LCD_SIZE_Y - 13, text_nl_overflow_y));

    /* 预留接口目前为桩函数，返回 0 */
    ASSERT_EQ_INT(0, Lcd_ShowTest(0, 0, text_a));

    return 0;
}

/* -------------------------------------------------------------------------
 * 测试入口:
 *   按固定顺序执行所有子用例，任一失败立即返回 1。
 * 约定:
 *   返回 0 表示该测试程序通过。
 * ------------------------------------------------------------------------- */
int main(void)
{
    if (test_lcd_init_and_pixel_rw() != 0) return 1;
    if (test_fillrect_and_invert() != 0) return 1;
    if (test_lineh_linev() != 0) return 1;
    if (test_utf8_next_decode() != 0) return 1;
    if (test_showstr_ascii_utf8_newline_and_bounds() != 0) return 1;
    return 0;
}
