/* gradient_check.c —— **渐变自检**（用户 2026-10-07：「渐变好像失效了」→「专门写个程序测试渐变」）
 * gradient_check.c -- **Gradient self-check** (user report, 2026-10-07)
 *
 * ## 为什么单独写一个
 * ## Why a dedicated program
 *
 * `draw_brush.c` 那类"体检"图**靠肉眼看**，而桌面与设备看到的不是一回事。这里改成：
 * **每条判据画一条自己的带**，并且**把结论同时打印出来、也画在屏幕上** ——
 * 打印那份给无头跑（`vmlcli`）用，屏幕那份给设备抓帧用，两边读的是同一批字。
 * The picture-based checks rely on eyes; this one gives every criterion its own band and reports
 * both to stdout (for headless `vmlcli`) and on screen (for device captures) -- same text, two exits.
 *
 * ## 判据
 * ## Criteria
 *
 *   G0_PROBE    取证本身有效吗：画一个**已知纯色**再读回（见下"哨兵"）
 *   G1_LINEAR   `ui_2d_gradient` + `ui_2d_rect_grad`：**左端红、右端蓝**
 *   G2_RADIAL   同一套换 `radial=1`：圆心与边缘不同
 *   G3_CIRCLE   `ui_2d_circle_grad`：圆心与边缘不同
 *   G4_PEN      `ui_brush_linear` + `ui_brush_set_pen`：**描边**用渐变
 *   G5_POLY     `ui_2d_polyline(…, grad)`：折线描边用渐变
 *   G6_SOLID    对照：纯色矩形**两端应当相同**（这条若 FAIL 说明取样本身不可信）
 *
 * ⚠ **接口是照着 `Lib/c/vml_ui.h` 核过的**，不是猜的：渐变填充有自己的入口
 * （`ui_2d_rect_grad` / `ui_2d_circle_grad`，直接收渐变**名字**），
 * 而 `ui_2d_rect(x,y,w,h,color,fill,lw,radius)` 收的是**颜色**、不吃刷子。
 *
 * ## 哨兵：`0` 是合法颜色，不能拿它当"读不回来"
 * ## Sentinel: 0 is a legal color, do not use it as "unreadable"
 *
 * 第一版用「两端都读到 0 ⇒ 记 SKIP(未取证)」来判断读回可不可用 —— **错了**：
 * 渐变失效时画出来的是**纯黑**，读回**正好就是 0**，于是**真失败被吞成"未取证"**，
 * 一条都没报错（本仓 E142：缺席判据要先过"取证本身有效"）。
 * 正解 = **先画一个已知纯色并读回**（`G0_PROBE`），它过了才说明读回可信；
 * 之后"读到 0"就是"这里真的是黑的"，**该报 FAIL 就报 FAIL**。
 * The first version treated "both ends read 0" as "read-back unavailable" -- wrong: a broken
 * gradient paints solid black, which reads back as exactly 0, so the real failure was swallowed as
 * SKIP. `G0_PROBE` draws a known solid color first; only if that reads back is a 0 believed.
 *
 * ## 实测（2026-10-07，桌面 `vmlcli`，480×640）
 * ## Measured (2026-10-07, desktop `vmlcli`, 480x640)
 *
 *   G1/G2/G3 画出来的是**纯黑**（几何正确、填充是 0）⇒ 渐变解析成了"没有颜色"
 *   G4/G5    **什么都没画**（带子里只有底色）
 *   G6       正常（255,48,48）
 *   读回     **可用**（G6 与 PROBE 都读到了真值）
 */

#include <vml_ui.h>

#define RED_A    0xFFFF3030     /* 红 / red */
#define BLUE_B   0xFF3030FF     /* 蓝 / blue */
#define PROBE_C  0xFF20C040     /* 哨兵用的绿（取值刻意**既不是**红/蓝，也**不是底色**） */
#define BG       0xFF101018

static int fails, total, line, sw2, sh2, probe_ok;

/* 放一行结论：屏幕上一条（设备抓帧看），stdout 一条（无头跑看）。 */
/* One verdict line: one on screen (device capture), one on stdout (headless). */
static void say(const char* key, const char* state, int bad)
{
    ui_2d_text(8, line, key, bad ? 0xFFE05050 : 0xFF40D060, 13, VML_ANCHOR_LEFT);
    ui_2d_text(120, line, state, bad ? 0xFFE05050 : (state[0] == 'S' ? 0xFF909098 : 0xFF40D060), 13, VML_ANCHOR_LEFT);
    line += 14;
}

static int rgb(int c) { return c & 0xFFFFFF; }

static int differs(int c1, int c2)
{
    int dr = ((c1 >> 16) & 255) - ((c2 >> 16) & 255);
    int dg = ((c1 >> 8) & 255) - ((c2 >> 8) & 255);
    int db = (c1 & 255) - ((c2 & 255));
    if (dr < 0) dr = -dr;
    if (dg < 0) dg = -dg;
    if (db < 0) db = -db;
    return dr + dg + db > 60;
}

/* 通用判据：取两点比一比。`must_differ` = 渐变类（两端必须不同）/ 对照类（必须相同）。 */
/* Generic: compare two points; must_differ for gradients, must-match for the control. */
static void verdict(const char* key, int x1, int y1, int x2, int y2, int must_differ)
{
    int a, b, d, bad;
    const char* state;

    total++;
    ui_2d_present();
    a = ui_2d_get_pixel(x1, y1);
    b = ui_2d_get_pixel(x2, y2);
    d = differs(a, b);

    if (!probe_ok) {
        state = "SKIP(读回不可用)";
        bad = 0;
    } else if (must_differ ? !d : d) {
        state = "FAIL";
        bad = 1;
        fails++;
    } else {
        state = "OK";
        bad = 0;
    }
    say(key, state, bad);
    printf("%s=%s a=%06X b=%06X (x1=%d,y1=%d x2=%d,y2=%d)\n",
           key, state, rgb(a), rgb(b), x1, y1, x2, y2);
    ui_2d_present();
}

int main(void)
{
    int sw, sh, band, cy, r;
    int pts[8];

    ui_scr_set_keep_on(1);
    /* 尺寸交给宿主（0,0 = 用默认值）；**开窗之后再问一次**才是真实画布 */
    /* Let the host size it (0,0 = defaults); ask again *after* opening for the real canvas. */
    ui_win_open_ex("渐变自检 / gradient check", 0, 0, VML_WIN_PORTRAIT, VML_WIN_NO_GAMEPAD);
    sw = ui_scr_get_w();
    sh = ui_scr_get_h();
    if (sw <= 0 || sh <= 0) { sw = 320; sh = 480; }
    sw2 = sw; sh2 = sh;

    band = (sh - 40) / 8;          /* 8 条带：G0 探测 + G1..G6 + 总账 */
    if (band < 12) band = 12;
    line = band * 7 + 4;           /* 结论文字从第 7 条带开始，避开图元 */
    ui_2d_clear(BG);

    /* ── G0 哨兵：画一个**已知纯色**再读回。它过了，"读到 0"才可信 ── */
    /* ── G0 sentinel: draw a known solid color and read it back ── */
    ui_2d_rect(0, 4, sw, 8, PROBE_C, 1, 0, 0);
    ui_2d_present();
    {
        int p = ui_2d_get_pixel(3, 8);
        probe_ok = (rgb(p) == rgb(PROBE_C));   /* 精确比对；底色/黑都算不通过 */
        printf("G0_PROBE=%s 读到 %06X 期望 %06X\n",
               probe_ok ? "OK" : "FAIL(读回不可用)", rgb(p), rgb(PROBE_C));
        if (!probe_ok) { fails++; total++; }
        say("G0_PROBE", probe_ok ? "OK" : "FAIL(读回不可用)", !probe_ok);
        ui_2d_present();
    }

    /* ── G1 线性渐变：定义 + `ui_2d_rect_grad` 填充 ── */
    /* ── G1 linear: define it, then fill a rect with it ── */
    ui_2d_gradient("g1", 0, RED_A, BLUE_B, 0, 0, 1000, 0);
    ui_2d_rect_grad(0, band * 1, sw, band, "g1", 0);
    verdict("G1_LINEAR", 3, band * 1 + band / 2, sw - 4, band * 1 + band / 2, 1);

    /* ── G2 径向渐变（同一个 id 换 radial=1 重定义）── */
    /* ── G2 radial (redefine the id with radial=1) ── */
    ui_2d_gradient("g2", 1, RED_A, BLUE_B, 0, 0, 0, 0);
    ui_2d_rect_grad(0, band * 2, sw, band, "g2", 0);
    cy = band * 2 + band / 2;
    verdict("G2_RADIAL", sw / 2, cy, sw - 6, cy, 1);

    /* ── G3 渐变圆 ── */
    /* ── G3 gradient-filled circle ── */
    r = band / 2 - 2;
    if (r < 4) r = 4;
    cy = band * 3 + band / 2;
    ui_2d_circle_grad(sw / 2, cy, r, "g1");
    verdict("G3_CIRCLE", sw / 2, cy, sw / 2 + r - 3, cy, 1);

    /* ── G4 渐变**画笔**（描边）：`ui_brush_linear` + `ui_brush_set_pen` ── */
    /* ── G4 gradient *pen* (stroke) ── */
    {
        int b = ui_brush_linear(RED_A, BLUE_B, 0, 0, 1000, 0);
        ui_brush_set_pen(b, 8, 0, 0, 0);
        ui_2d_rect(4, band * 4 + 6, sw - 8, band - 12, 0, 0, 8, 0);
        ui_brush_reset();
        /* ⚠ 取**描边中心线**（矩形从 `band*4+6` 起、描边宽 8 ⇒ 中心在 `+10`），
         *   不取靠近上边缘的 `+8`：那里离边只有 2px，而各平台取整/偏移差 1~3px 就会掉进底色
         *   ⇒ 两端都读到背景色、判成"没有渐变"（**实测：同一份代码桌面绿、iOS 红**，是判据取样点太脆）。 */
        /* ⚠ Sample the **centre line of the stroke** (rect starts at `band*4+6` with an 8px stroke
         *   => centre at `+10`), not `+8` near the top edge: 2px from the edge means a 1-3px
         *   platform rounding difference drops into the background and reports "no gradient"
         *   (**measured: same source, green on desktop, red on iOS** -- the sample point was brittle). */
        verdict("G4_PEN", 3, band * 4 + 10, sw - 4, band * 4 + 10, 1);
    }

    /* ── G5 折线描边用渐变（`ui_2d_polyline(pts, count, stroke, width, grad)`）── */
    /* ── G5 polyline stroked with a gradient ── */
    pts[0] = 4;             pts[1] = band * 5 + band / 2;
    pts[2] = sw / 3;        pts[3] = band * 5 + 6;
    pts[4] = sw * 2 / 3;    pts[5] = band * 5 + band - 6;
    pts[6] = sw - 4;        pts[7] = band * 5 + band / 2;
    ui_2d_polyline(pts, 4, 1, 6, "g1");
    verdict("G5_POLY", 4, band * 5 + band / 2, sw - 4, band * 5 + band / 2, 1);

    /* ── G6 对照：纯色两端应当相同 ── */
    /* ── G6 control: a solid fill must read the same at both ends ── */
    ui_2d_rect(0, band * 6, sw, band, RED_A, 1, 0, 0);
    verdict("G6_SOLID", 3, band * 6 + band / 2, sw - 4, band * 6 + band / 2, 0);

    /* 屏上也写一行总账 */
    /* A one-line summary on screen as well. */
    ui_2d_text(sw / 2, sh - 10, fails ? "SOME FAILED" : "ALL OK",
               fails ? 0xFFE05050 : 0xFF40D060, 16, VML_ANCHOR_CENTER);
    printf("GRADIENT_SUMMARY total=%d fails=%d probe_ok=%d\n", total, fails, probe_ok);
    ui_2d_present();

    ui_win_close();
    return fails;
}
