/* -------------------------------------------------------------------------
 * 文件名: lcd_draw.c
 * 模块职责:
 *   提供基于像素接口的基础绘图能力，包含：
 *   1) 矩形填充（Lcd_FillRect）
 *   2) 水平线绘制（Lcd_LineH）
 *   3) 垂直线绘制（Lcd_LineV）
 *   4) 区域反显（Lcd_Invert）
 *
 * 设计说明:
 *   - 本模块是 lcd.c 的上层，复用 Lcd_SetPixel/Lcd_GetFrameBuffer。
 *   - 当前模块在入口做参数合法性检查，发生越界/非法颜色时直接返回错误码。
 *   - 坐标区间语义在不同函数中不同，使用时需特别注意（见各函数注释）。
 * ------------------------------------------------------------------------- */
#include "lcd_draw.h"
#include <stdlib.h>

#ifdef DEBUG
#include <stdio.h>
#endif

static int8_t Lcd_ColorCheck(uint32_t color)
{
    if ((color != LCD_FONT) && (color != LCD_BACK)) {
        return LCD_ERR_INVALID_COLOR;
    }
    return LCD_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: Lcd_FillRect
 * 功能:
 *   用指定颜色填充一个矩形区域。
 *
 * 参数:
 *   left_x   - 左上角 x（包含）
 *   top_y    - 左上角 y（包含）
 *   right_x  - 右下角 x（包含）
 *   bottom_y - 右下角 y（包含）
 *   color    - 填充颜色
 *
 * 区间语义:
 *   - 采用“闭区间”遍历：
 *       x: [left_x, right_x]
 *       y: [top_y,  bottom_y]
 *
 * 注意:
 *   - 若 right_x < left_x 或 bottom_y < top_y，当前实现不会进入循环（或可能因无符号导致异常遍历风险），
 *     调用方应保证坐标顺序合法。
 * 返回值:
 *   - LCD_OK: 参数合法且绘制完成
 *   - LCD_ERR_OUT_OF_RANGE: 坐标越界或顺序非法
 *   - LCD_ERR_INVALID_COLOR: 颜色非法
 * ------------------------------------------------------------------------- */
int8_t Lcd_FillRect(uint16_t left_x, uint16_t top_y, uint16_t right_x, uint16_t bottom_y, uint8_t color)
{
    ASSERT((right_x >= LCD_SIZE_X) || (bottom_y >= LCD_SIZE_Y));
    if (Lcd_ColorCheck(color) != LCD_OK) return LCD_ERR_INVALID_COLOR;
    ASSERT((right_x >= LCD_SIZE_X) || (bottom_y >= LCD_SIZE_Y));
    if ((right_x < left_x) || (bottom_y < top_y)) return LCD_ERR_OUT_OF_RANGE;
    ASSERT((right_x >= LCD_SIZE_X) || (bottom_y >= LCD_SIZE_Y));
    if ((right_x >= LCD_SIZE_X) || (bottom_y >= LCD_SIZE_Y)) return LCD_ERR_OUT_OF_RANGE;

    for (uint16_t y = top_y; y <= bottom_y; y++) {
        for (uint16_t x = left_x; x <= right_x; x++) {
            Lcd_SetPixel(x, y, color);
        }
    }
    return LCD_OK;
}

/* 宏: M_GuiSwap
 * 作用:
 *   使用异或交换法交换两个同类型变量的值。
 * 使用场景:
 *   在起止坐标反向时进行修正（如 xStart > xEnd）。
 * 注意:
 *   该写法要求 a 与 b 指向不同变量；若传入同一变量会把值清零。
 */
#define M_GuiSwap(a, b) { a ^= b; b ^= a; a ^= b; }

/* -------------------------------------------------------------------------
 * 函数名: Lcd_LineH
 * 功能:
 *   绘制水平线（可设置线宽）。
 *
 * 参数:
 *   wXStart - 起始 x
 *   wXEnd   - 结束 x
 *   wYStart - 起始 y
 *   wWidth  - 线宽（沿 y 方向扩展）
 *   color   - 线条颜色
 *
 * 区间语义:
 *   - x 方向采用半开区间 [wXStart, wXEnd)
 *   - y 方向采用半开区间 [wYStart, wYStart + wWidth)
 *
 * 说明:
 *   - 当 wXEnd < wXStart 时会自动交换两者，支持反向输入。
 * 返回值:
 *   - LCD_OK: 参数合法且绘制完成
 *   - LCD_ERR_OUT_OF_RANGE: 坐标越界
 *   - LCD_ERR_INVALID_COLOR: 颜色非法
 * ------------------------------------------------------------------------- */
int8_t Lcd_LineH(uint16_t wXStart, uint16_t wXEnd, uint16_t wYStart, uint16_t wWidth, uint8_t color)
{
    uint32_t wYEnd32;
    uint16_t wYEnd;

    if (Lcd_ColorCheck(color) != LCD_OK) return LCD_ERR_INVALID_COLOR;
    if (wXEnd < wXStart) {
        M_GuiSwap(wXEnd, wXStart);
    }
    wYEnd32 = (uint32_t)wYStart + (uint32_t)wWidth;
    ASSERT((wXStart >= LCD_SIZE_X) || (wXEnd > LCD_SIZE_X));
    if (wXStart >= LCD_SIZE_X) return LCD_ERR_OUT_OF_RANGE;
    ASSERT((wXEnd > LCD_SIZE_X) || (wYStart >= LCD_SIZE_Y));
    if (wXEnd > LCD_SIZE_X) return LCD_ERR_OUT_OF_RANGE; /* 半开区间，end 可等于边界 */
    ASSERT((wYStart >= LCD_SIZE_Y) || (wYEnd32 > LCD_SIZE_Y));
    if (wYStart >= LCD_SIZE_Y) return LCD_ERR_OUT_OF_RANGE;
    ASSERT((wYEnd32 > LCD_SIZE_Y) || (wXStart >= LCD_SIZE_X));
    if (wYEnd32 > LCD_SIZE_Y) return LCD_ERR_OUT_OF_RANGE; /* 半开区间，end 可等于边界 */
    wYEnd = (uint16_t)wYEnd32;

    for (uint16_t x = wXStart; x < wXEnd; x++) {
        for (uint16_t y = wYStart; y < wYEnd; y++) {
            Lcd_SetPixel(x, y, color);
        }
    }
    return LCD_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: Lcd_LineV
 * 功能:
 *   绘制垂直线（可设置线宽）。
 *
 * 参数:
 *   wYStart - 起始 y
 *   wYEnd   - 结束 y
 *   wXStart - 起始 x
 *   wWidth  - 线宽（沿 x 方向扩展）
 *   color   - 线条颜色
 *
 * 区间语义:
 *   - y 方向采用半开区间 [wYStart, wYEnd)
 *   - x 方向采用半开区间 [wXStart, wXStart + wWidth)
 *
 * 说明:
 *   - 当 wYEnd < wYStart 时会自动交换两者，支持反向输入。
 *
 * 返回值:
 *   - LCD_OK: 参数合法且绘制完成
 *   - LCD_ERR_OUT_OF_RANGE: 坐标越界
 *   - LCD_ERR_INVALID_COLOR: 颜色非法
 * ------------------------------------------------------------------------- */
int8_t Lcd_LineV(uint16_t wYStart, uint16_t wYEnd, uint16_t wXStart, uint16_t wWidth, uint8_t color)
{
    uint32_t wXEnd32;
    uint16_t wXEnd;

    if (Lcd_ColorCheck(color) != LCD_OK) return LCD_ERR_INVALID_COLOR;
    if (wYEnd < wYStart) {
        M_GuiSwap(wYEnd, wYStart);
    }
    wXEnd32 = (uint32_t)wXStart + (uint32_t)wWidth;
    ASSERT((wYStart >= LCD_SIZE_Y) || (wXStart >= LCD_SIZE_X));
    if (wYStart >= LCD_SIZE_Y) return LCD_ERR_OUT_OF_RANGE;
    ASSERT((wYEnd > LCD_SIZE_Y) || (wXStart >= LCD_SIZE_X));
    if (wYEnd > LCD_SIZE_Y) return LCD_ERR_OUT_OF_RANGE; /* 半开区间，end 可等于边界 */
    ASSERT((wXStart >= LCD_SIZE_X) || (wXEnd32 > LCD_SIZE_X));
    if (wXStart >= LCD_SIZE_X) return LCD_ERR_OUT_OF_RANGE;
    ASSERT((wXEnd32 > LCD_SIZE_X) || (wYStart >= LCD_SIZE_Y));
    if (wXEnd32 > LCD_SIZE_X) return LCD_ERR_OUT_OF_RANGE; /* 半开区间，end 可等于边界 */
    wXEnd = (uint16_t)wXEnd32;

    for (uint16_t x = wXStart; x < wXEnd; x++) {
        for (uint16_t y = wYStart; y < wYEnd; y++) {
            Lcd_SetPixel(x, y, color);
        }
    }
    return LCD_OK;
}

/* -------------------------------------------------------------------------
 * 函数名: Lcd_Line
 * 功能:
 *   绘制任意方向线段（可设置线宽）。
 * 说明:
 *   - 水平/垂直线走 Lcd_LineH/Lcd_LineV 快速路径。
 *   - 斜线采用 Bresenham 风格误差迭代，并做对称双端绘制。
 * ------------------------------------------------------------------------- */
int8_t Lcd_Line(uint16_t wXstart, uint16_t wYstart, uint16_t wXend, uint16_t wYend, uint16_t wWidth, uint8_t color)
{
    uint16_t wDX, wDY, wSignY, wSignX, wDecision;
    uint16_t wCurx, wCury, wNextx, wNexty, wPY, wPX;

    if (Lcd_ColorCheck(color) != LCD_OK) return LCD_ERR_INVALID_COLOR;
    if (wYstart == wYend) return Lcd_LineH(wXstart, wXend, wYstart, wWidth, color);
    if (wXstart == wXend) return Lcd_LineV(wYstart, wYend, wXstart, wWidth, color);

    wDX = (uint16_t)abs((int)wXstart - (int)wXend);
    wDY = (uint16_t)abs((int)wYstart - (int)wYend);
    if (((wDX >= wDY && (wXstart > wXend)) || ((wDY > wDX) && (wYstart > wYend)))) {
        M_GuiSwap(wXend, wXstart);
        M_GuiSwap(wYend, wYstart);
    }
    wSignY = (wYend - wYstart) / wDY;
    wSignX = (wXend - wXstart) / wDX;

    if (wDX >= wDY) {
        wCurx = wXstart;
        wCury = wYstart;
        wNextx = wXend;
        wNexty = wYend;
        wDecision = (wDX >> 1);
        for (; wCurx <= wNextx;) {
            if (wDecision >= wDX) {
                wDecision -= wDX;
                wCury += wSignY;
                wNexty -= wSignY;
            }
            for (wPY = wCury - wWidth / 2; wPY <= wCury + wWidth / 2; wPY++) {
                Lcd_SetPixel(wCurx, wPY, color);
            }
            for (wPY = wNexty - wWidth / 2; wPY <= wNexty + wWidth / 2; wPY++) {
                Lcd_SetPixel(wNextx, wPY, color);
            }
            wCurx++;
            wNextx--;
            wDecision += wDY;
        }
    } else {
        wCurx = wXstart;
        wCury = wYstart;
        wNextx = wXend;
        wNexty = wYend;
        wDecision = (wDY >> 1);
        for (; wCury <= wNexty;) {
            if (wDecision >= wDY) {
                wDecision -= wDY;
                wCurx += wSignX;
                wNextx -= wSignX;
            }
            for (wPX = wCurx - wWidth / 2; wPX <= wCurx + wWidth / 2; wPX++) {
                Lcd_SetPixel(wPX, wCury, color);
            }
            for (wPX = wNextx - wWidth / 2; wPX <= wNextx + wWidth / 2; wPX++) {
                Lcd_SetPixel(wPX, wNexty, color);
            }
            wCury++;
            wNexty--;
            wDecision += wDX;
        }
    }

    return LCD_OK;
}

/* 宏: M_Max / M_Min
 * 作用:
 *   计算两个值的较大/较小值，用于将任意方向输入规范化为最小/最大边界。
 */
#define M_Max(a, b) (((a) > (b)) ? (a) : (b))
#define M_Min(a, b) (((a) < (b)) ? (a) : (b))

/* -------------------------------------------------------------------------
 * 函数名: Lcd_Invert
 * 功能:
 *   对指定矩形区域像素执行按位取反，实现反显效果。
 *
 * 参数:
 *   wXstart, wYstart - 第一个角点
 *   wXend,   wYend   - 第二个角点
 *
 * 区间语义:
 *   - 先通过 min/max 规范化坐标后，按半开区间处理：
 *       x: [xMin, xMax)
 *       y: [yMin, yMax)
 *
 * 边界处理:
 *   - 若任一端点超出屏幕范围（> LCD_SIZE_X / > LCD_SIZE_Y），函数返回错误码。
 *   - 这里通过直接访问 framebuffer 提升效率，不逐点调用 Lcd_SetPixel。
 *
 * 注意:
 *   - 本函数使用线性地址 `y * LCD_SIZE_X + x` 访问显存，默认与 lcd.c 的布局一致。
 * 返回值:
 *   - LCD_OK: 参数合法且处理完成
 *   - LCD_ERR_OUT_OF_RANGE: 坐标越界
 * ------------------------------------------------------------------------- */
int8_t Lcd_Invert(uint16_t wXstart, uint16_t wYstart, uint16_t wXend, uint16_t wYend)
{
    uint16_t xMin, xMax, yMin, yMax;
    uint8_t wColor;
    uint8_t *framebuffer = Lcd_GetFrameBuffer();

    if ((wXstart > LCD_SIZE_X) || (wXend > LCD_SIZE_X)) return LCD_ERR_OUT_OF_RANGE;
    if ((wYstart > LCD_SIZE_Y) || (wYend > LCD_SIZE_Y)) return LCD_ERR_OUT_OF_RANGE;

    xMin = M_Min(wXstart, wXend);
    yMin = M_Min(wYstart, wYend);
    xMax = M_Max(wXstart, wXend);
    yMax = M_Max(wYstart, wYend);

    for (uint16_t y = yMin; y < yMax; y++) {
        for (uint16_t x = xMin; x < xMax; x++) {
            wColor = framebuffer[y * LCD_SIZE_X + x];
            framebuffer[y * LCD_SIZE_X + x] = (uint8_t)~wColor;
        }
    }
    return LCD_OK;
}
