#include <Imlib2.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include <renderer.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>

/* ═══════════════════════════════════════════════════════════════════════════
   Colour helpers
   ═══════════════════════════════════════════════════════════════════════════ */

static unsigned long alloc_rgb(Display *dpy, int screen, int r, int g, int b)
{
    XColor c;
    c.red   = (unsigned short)(r << 8);
    c.green = (unsigned short)(g << 8);
    c.blue  = (unsigned short)(b << 8);
    c.flags = DoRed | DoGreen | DoBlue;
    if (!XAllocColor(dpy, DefaultColormap(dpy, screen), &c))
        return (r+g+b > 383) ? WhitePixel(dpy,screen) : BlackPixel(dpy,screen);
    return c.pixel;
}

static unsigned long lerp_rgb(Display *dpy, int screen,
                               int r1,int g1,int b1,
                               int r2,int g2,int b2, float t)
{
    int r = (int)(r1 + (r2-r1)*t);
    int g = (int)(g1 + (g2-g1)*t);
    int b = (int)(b1 + (b2-b1)*t);
    return alloc_rgb(dpy, screen, r, g, b);
}

/* ═══════════════════════════════════════════════════════════════════════════
   Init / free
   ═══════════════════════════════════════════════════════════════════════════ */

void renderer_init(RenderState *rs, Display *dpy, Window win,
                   int screen, int sw, int sh, AppList *apps)
{
    memset(rs, 0, sizeof(*rs));
    rs->dpy    = dpy;
    rs->win    = win;
    rs->screen = screen;
    rs->sw     = sw;
    rs->sh     = sh;
    rs->apps   = apps;

    /* Runtime footer height */
    rs->footer_h = (int)(sh * FOOTER_H_FRAC);
    if (rs->footer_h < 100) rs->footer_h = 100;

    /* ── Wii authentic light palette ─────────────────────────────────────── */
    WiiColors *c = &rs->col;
    c->bg               = alloc_rgb(dpy, screen, 255, 255, 255);
    c->bg2              = alloc_rgb(dpy, screen, 195, 198, 204);
    c->tile_face        = alloc_rgb(dpy, screen, 242, 244, 246);
    c->tile_shadow_dark = alloc_rgb(dpy, screen, 140, 143, 150);
    c->tile_shadow_mid  = alloc_rgb(dpy, screen, 175, 178, 185);
    c->tile_border_focus= alloc_rgb(dpy, screen,  90, 195, 235);
    c->tile_border_norm = alloc_rgb(dpy, screen, 195, 198, 205);
    c->tile_inner_hi    = alloc_rgb(dpy, screen, 255, 255, 255);
    c->footer_bg        = alloc_rgb(dpy, screen, 225, 228, 234);
    c->footer_border    = alloc_rgb(dpy, screen, 127, 216, 246);
    c->text_dark        = alloc_rgb(dpy, screen,  50,  50,  60);
    c->text_med         = alloc_rgb(dpy, screen, 110, 115, 125);
    c->text_light       = alloc_rgb(dpy, screen, 160, 165, 175);
    c->clock_text       = alloc_rgb(dpy, screen, 120, 125, 140);
    c->white            = alloc_rgb(dpy, screen, 255, 255, 255);
    c->tooltip_bg       = alloc_rgb(dpy, screen, 248, 250, 252);
    c->tooltip_border   = alloc_rgb(dpy, screen,  90, 195, 235);
    c->nav_arrow_bg     = alloc_rgb(dpy, screen, 165, 215, 240);
    c->nav_arrow_fg     = alloc_rgb(dpy, screen, 255, 255, 255);

    /* ── fonts ────────────────────────────────────────────────────────────── */
    rs->font_title = XLoadQueryFont(dpy,
        "-*-helvetica-bold-r-normal-*-14-*-*-*-*-*-*-*");
    if (!rs->font_title)
        rs->font_title = XLoadQueryFont(dpy,
            "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*");
    if (!rs->font_title)
        rs->font_title = XLoadQueryFont(dpy, "fixed");

    rs->font_sub = XLoadQueryFont(dpy,
        "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
    if (!rs->font_sub)
        rs->font_sub = XLoadQueryFont(dpy, "fixed");

    rs->font_clock = XLoadQueryFont(dpy,
        "-*-helvetica-medium-r-normal-*-64-*-*-*-*-*-*-*");
    if (!rs->font_clock)
        rs->font_clock = XLoadQueryFont(dpy,
            "-*-helvetica-bold-r-normal-*-64-*-*-*-*-*-*-*");
    if (!rs->font_clock)
        rs->font_clock = XLoadQueryFont(dpy, "fixed");

    rs->font_clock_ampm = XLoadQueryFont(dpy,
        "-*-helvetica-medium-r-normal-*-32-*-*-*-*-*-*-*");
    if (!rs->font_clock_ampm)
        rs->font_clock_ampm = XLoadQueryFont(dpy, "fixed");

    rs->font_date = XLoadQueryFont(dpy,
        "-*-helvetica-medium-r-normal-*-64-*-*-*-*-*-*-*");
    if (!rs->font_date)
        rs->font_date = XLoadQueryFont(dpy, "fixed");

    /* ── GC ──────────────────────────────────────────────────────────────── */
    XGCValues gv;
    gv.foreground = c->bg;
    gv.background = c->bg;
    rs->gc = XCreateGC(dpy, win, GCForeground | GCBackground, &gv);

    /* ── back buffer ─────────────────────────────────────────────────────── */
    rs->backbuf = XCreatePixmap(dpy, win, sw, sh,
                                 DefaultDepth(dpy, screen));

    /* ── runtime tile dimensions ────────────────────────────────────────── */
    /* Divide the usable area (minus footer) into COLS x ROWS cells with a
       fixed gap fraction.  We target a 10:7 (w:h) aspect ratio per tile. */
    int usable_h = sh - rs->footer_h;
    /* Gap is 2% of the narrower screen dimension */
    rs->tile_gap = (int)((sw < usable_h ? sw : usable_h) * 0.02f);
    if (rs->tile_gap < 6) rs->tile_gap = 6;
    /* Tile width fills the screen width minus gaps, with small side margins */
    int side_margin = rs->tile_gap * 3;
    rs->tile_w   = (sw - 2*side_margin - (COLS-1)*rs->tile_gap) / COLS;
    /* Height maintains 10:7 aspect ratio */
    rs->tile_h   = rs->tile_w * 7 / 10;
    /* If that doesn't fit vertically, shrink to fit rows */
    int top_margin = rs->tile_gap * 2;
    int max_tile_h = (usable_h - 2*top_margin - (ROWS-1)*rs->tile_gap) / ROWS;
    if (rs->tile_h > max_tile_h) {
        rs->tile_h = max_tile_h;
        rs->tile_w = rs->tile_h * 10 / 7;
    }
    rs->tile_r = rs->tile_w / 14;   /* corner radius scales with tile width */
    if (rs->tile_r < 6) rs->tile_r = 6;

    /* ── Xft TTF clock font ──────────────────────────────────────────────── */
    rs->xft_clock = NULL;
    rs->xft_draw  = NULL;

    rs->ttf_clock_path = "/home/mineman/.local/share/fonts/dseg7-classic-latin-400-normal.ttf";

    if (rs->ttf_clock_path) {
        /* Clock height: roughly 60% of the footer dip depth */
        int clock_px = (int)(sh * FOOTER_H_FRAC * 0.28f * 0.85f);
        if (clock_px < 16) clock_px = 16;
        rs->xft_clock = XftFontOpen(dpy, screen,
                            XFT_FILE,   XftTypeString,  rs->ttf_clock_path,
                            XFT_PIXEL_SIZE, XftTypeDouble, (double)clock_px,
                            XFT_ANTIALIAS,  XftTypeBool,   True,
                            NULL);
        if (!rs->xft_clock)
            fprintf(stderr, "renderer: could not load TTF '%s'\n",
                    rs->ttf_clock_path);
        else
            rs->xft_draw = XftDrawCreate(dpy, rs->backbuf,
                               DefaultVisual(dpy, screen),
                               DefaultColormap(dpy, screen));
    }

    /* ── grid origin ─────────────────────────────────────────────────────── */
    int grid_w  = COLS * rs->tile_w + (COLS - 1) * rs->tile_gap;
    int grid_h  = ROWS * rs->tile_h + (ROWS - 1) * rs->tile_gap;
    rs->grid_x  = (sw - grid_w) / 2;
    rs->grid_y  = (usable_h - grid_h) / 2;
    if (rs->grid_y < 8) rs->grid_y = 8;
}

void renderer_free(RenderState *rs)
{
    if (rs->font_title)      XFreeFont(rs->dpy, rs->font_title);
    if (rs->font_sub)        XFreeFont(rs->dpy, rs->font_sub);
    if (rs->font_clock)      XFreeFont(rs->dpy, rs->font_clock);
    if (rs->font_clock_ampm) XFreeFont(rs->dpy, rs->font_clock_ampm);
    if (rs->font_date)       XFreeFont(rs->dpy, rs->font_date);
    if (rs->xft_draw)  XftDrawDestroy(rs->xft_draw);
    if (rs->xft_clock) XftFontClose(rs->dpy, rs->xft_clock);
    XFreeGC(rs->dpy, rs->gc);
    XFreePixmap(rs->dpy, rs->backbuf);
}

/* ═══════════════════════════════════════════════════════════════════════════
   Animation
   ═══════════════════════════════════════════════════════════════════════════ */

void renderer_tick(RenderState *rs)
{
    rs->glow_phase = (rs->glow_phase + 5) & 0xFF;

    if (rs->scroll_anim != rs->scroll_target) {
        int diff = rs->scroll_target - rs->scroll_anim;
        int step = diff / 4;
        if (step == 0) step = (diff > 0) ? 1 : -1;
        rs->scroll_anim += step;
        if (abs(rs->scroll_anim - rs->scroll_target) < 2)
            rs->scroll_anim = rs->scroll_target;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Focus navigation
   ═══════════════════════════════════════════════════════════════════════════ */

int renderer_move_focus(RenderState *rs, int dcol, int drow)
{
    int n = rs->apps->count;
    if (n == 0) return 0;

    int total_rows = (n + COLS - 1) / COLS;

    int cur_col = rs->focus_idx % COLS;
    int cur_row = rs->focus_idx / COLS;

    int new_col = cur_col + dcol;
    int new_row = cur_row + drow;

    if (new_col < 0)           new_col = 0;
    if (new_col >= COLS)       new_col = COLS - 1;
    if (new_row < 0)           new_row = 0;
    if (new_row >= total_rows) new_row = total_rows - 1;

    int new_idx = new_row * COLS + new_col;
    if (new_idx >= n) new_idx = n - 1;
    if (new_idx == rs->focus_idx) return 0;

    rs->focus_idx  = new_idx;
    rs->glow_phase = 0;

    int vis_row = new_row - rs->scroll_col;
    if (vis_row < 0)
        rs->scroll_col = new_row;
    else if (vis_row >= ROWS)
        rs->scroll_col = new_row - ROWS + 1;

    rs->scroll_target = rs->scroll_col * (rs->tile_h + rs->tile_gap);

    return 1;
}

DesktopEntry *renderer_focused_entry(RenderState *rs)
{
    if (!rs->apps || rs->apps->count == 0) return NULL;
    return &rs->apps->entries[rs->focus_idx];
}

/* ═══════════════════════════════════════════════════════════════════════════
   Drawing primitives
   ═══════════════════════════════════════════════════════════════════════════ */

static void fill_rrect(Display *dpy, Drawable d, GC gc,
                        int x, int y, int w, int h, int r)
{
    if (r < 1) r = 1;
    if (2*r > w) r = w/2;
    if (2*r > h) r = h/2;
    XFillRectangle(dpy, d, gc, x+r,   y,   w-2*r, h);
    XFillRectangle(dpy, d, gc, x,     y+r, r,     h-2*r);
    XFillRectangle(dpy, d, gc, x+w-r, y+r, r,     h-2*r);
    XFillArc(dpy, d, gc, x,       y,       2*r, 2*r,  90*64,  90*64);
    XFillArc(dpy, d, gc, x+w-2*r, y,       2*r, 2*r,   0,     90*64);
    XFillArc(dpy, d, gc, x,       y+h-2*r, 2*r, 2*r, 180*64,  90*64);
    XFillArc(dpy, d, gc, x+w-2*r, y+h-2*r, 2*r, 2*r, 270*64,  90*64);
}

static void draw_rrect(Display *dpy, Drawable d, GC gc,
                        int x, int y, int w, int h, int r)
{
    if (r < 1) r = 1;
    XDrawLine(dpy, d, gc, x+r,   y,     x+w-r-1, y);
    XDrawLine(dpy, d, gc, x+r,   y+h-1, x+w-r-1, y+h-1);
    XDrawLine(dpy, d, gc, x,     y+r,   x,       y+h-r-1);
    XDrawLine(dpy, d, gc, x+w-1, y+r,   x+w-1,   y+h-r-1);
    XDrawArc(dpy, d, gc, x,       y,       2*r, 2*r,  90*64,  90*64);
    XDrawArc(dpy, d, gc, x+w-2*r, y,       2*r, 2*r,   0,     90*64);
    XDrawArc(dpy, d, gc, x,       y+h-2*r, 2*r, 2*r, 180*64,  90*64);
    XDrawArc(dpy, d, gc, x+w-2*r, y+h-2*r, 2*r, 2*r, 270*64,  90*64);
}

/* Centre a string, truncating with ellipsis */
static void draw_center_str(Display *dpy, Drawable d, GC gc,
                              XFontStruct *font,
                              int cx, int y, const char *str, int max_w)
{
    if (!str || !str[0] || !font) return;
    char buf[128];
    snprintf(buf, sizeof(buf), "%s", str);
    int tw;
    while ((tw = XTextWidth(font, buf, (int)strlen(buf))) > max_w
           && strlen(buf) > 4) {
        size_t l = strlen(buf);
        buf[l-1] = '\0';
        if (l > 3) { buf[l-2]='.'; buf[l-3]='.'; buf[l-4]='.'; }
    }
    tw = XTextWidth(font, buf, (int)strlen(buf));
    XDrawString(dpy, d, gc, cx - tw/2, y, buf, (int)strlen(buf));
}

static void draw_nav_arrow(RenderState *rs, Drawable d, int x, int y,
                             int w, int h, int left)
{
    Display   *dpy = rs->dpy;
    GC         gc  = rs->gc;
    WiiColors *c   = &rs->col;

    XSetForeground(dpy, gc, c->nav_arrow_bg);
    XFillArc(dpy, d, gc, x, y, w, h, 0, 360*64);
    XSetForeground(dpy, gc, c->tile_border_focus);
    XDrawArc(dpy, d, gc, x, y, w, h, 0, 360*64);

    XSetForeground(dpy, gc, c->nav_arrow_fg);
    int cx = x + w/2;
    int cy = y + h/2;
    int aw = w/4;
    int ah = h/3;
    XPoint pts[3];
    if (left) {
        pts[0].x = cx + aw/2; pts[0].y = cy - ah;
        pts[1].x = cx - aw;   pts[1].y = cy;
        pts[2].x = cx + aw/2; pts[2].y = cy + ah;
    } else {
        pts[0].x = cx - aw/2; pts[0].y = cy - ah;
        pts[1].x = cx + aw;   pts[1].y = cy;
        pts[2].x = cx - aw/2; pts[2].y = cy + ah;
    }
    XFillPolygon(dpy, d, gc, pts, 3, Convex, CoordModeOrigin);
}

static void draw_tooltip(RenderState *rs, Drawable d,
                          int tx, int ty, int tw, const char *label)
{
    Display   *dpy = rs->dpy;
    GC         gc  = rs->gc;
    WiiColors *c   = &rs->col;

    if (!label || !label[0] || !rs->font_sub) return;

    int text_w = XTextWidth(rs->font_sub, label, (int)strlen(label));
    int bw = text_w + TOOLTIP_PAD_X * 2;
    int bh = TOOLTIP_H;
    int bx = tx + tw/2 - bw/2;
    int by = ty + rs->tile_h + TOOLTIP_PAD_Y;

    if (bx < 4) bx = 4;
    if (bx + bw > rs->sw - 4) bx = rs->sw - 4 - bw;

    XSetForeground(dpy, gc, c->tile_shadow_dark);
    fill_rrect(dpy, d, gc, bx+3, by+3, bw, bh, 10);

    XSetForeground(dpy, gc, c->tooltip_bg);
    fill_rrect(dpy, d, gc, bx, by, bw, bh, 10);

    XSetForeground(dpy, gc, c->tooltip_border);
    draw_rrect(dpy, d, gc, bx, by, bw, bh, 10);

    XSetFont(dpy, gc, rs->font_sub->fid);
    XSetForeground(dpy, gc, c->text_dark);
    int ty2 = by + bh/2 + 5;
    XDrawString(dpy, d, gc, bx + TOOLTIP_PAD_X, ty2,
                label, (int)strlen(label));

    XSetForeground(dpy, gc, c->tooltip_bg);
    XPoint tri[3] = {
        { (short)(tx + tw/2 - 8), (short)(by) },
        { (short)(tx + tw/2 + 8), (short)(by) },
        { (short)(tx + tw/2),     (short)(by - 10) }
    };
    XFillPolygon(dpy, d, gc, tri, 3, Convex, CoordModeOrigin);
    XSetForeground(dpy, gc, c->tooltip_border);
    XDrawLines(dpy, d, gc, tri, 3, CoordModeOrigin);
    XSetForeground(dpy, gc, c->tooltip_bg);
    XDrawLine(dpy, d, gc, tri[0].x, tri[0].y, tri[1].x, tri[1].y);
}

/* Wii-style circular button in the footer */
static void draw_footer_btn(RenderState *rs, Drawable d,
                              int cx, int cy, int r,
                              unsigned long bg_col, unsigned long fg_col,
                              const char *label)
{
    Display *dpy = rs->dpy;
    GC       gc  = rs->gc;

    XSetForeground(dpy, gc, rs->col.tile_shadow_dark);
    XFillArc(dpy, d, gc, cx-r+2, cy-r+2, 2*r, 2*r, 0, 360*64);

    XSetForeground(dpy, gc, bg_col);
    XFillArc(dpy, d, gc, cx-r, cy-r, 2*r, 2*r, 0, 360*64);

    XSetForeground(dpy, gc, rs->col.white);
    XDrawArc(dpy, d, gc, cx-r+3, cy-r+3, 2*r-6, 2*r-6, 30*64, 120*64);

    if (label && label[0] && rs->font_sub) {
        XSetFont(dpy, gc, rs->font_sub->fid);
        XSetForeground(dpy, gc, fg_col);
        int tw = XTextWidth(rs->font_sub, label, (int)strlen(label));
        XDrawString(dpy, d, gc, cx - tw/2, cy + 5, label, (int)strlen(label));
    }
}

/* ── Your original draw_footer with thicker border ───────────────────────── */
static void draw_footer(
    RenderState *rs,
    Drawable d,
    int w,
    int h
) {
    Display *dpy = rs->dpy;
    GC       gc  = rs->gc;

    int fh     = rs->footer_h;
    int y_top  = h - fh;

    int side_flat = w * 0.12f;
    int dip_depth = fh * 0.28f;
    int curve_len = w * 0.20f;

    XPoint pts[256];
    int n = 0;

    pts[n++] = (XPoint){0, h};
    pts[n++] = (XPoint){0, y_top};
    pts[n++] = (XPoint){side_flat, y_top};

    /* Left curve downward */
    for (int i = 0; i <= 32; i++) {
        float t = i / 32.0f;
        float s = (1.0f - cosf(t * M_PI)) * 0.5f;
        int x = side_flat + (curve_len * t);
        int y = y_top + (dip_depth * s);
        pts[n++] = (XPoint){x, y};
    }

    int center_right = w - side_flat - curve_len;
    pts[n++] = (XPoint){center_right, y_top + dip_depth};

    /* Right curve upward */
    for (int i = 32; i >= 0; i--) {
        float t = i / 32.0f;
        float s = (1.0f - cosf(t * M_PI)) * 0.5f;
        int x = w - side_flat - (curve_len * t);
        int y = y_top + (dip_depth * s);
        pts[n++] = (XPoint){x, y};
    }

    pts[n++] = (XPoint){w, y_top};
    pts[n++] = (XPoint){w, h};

    /* Fill footer */
    XSetForeground(dpy, gc, 0xCECFD7);
    XFillPolygon(dpy, d, gc, pts, n, Complex, CoordModeOrigin);

    /* Draw thick blue highlight border (FOOTER_BORDER_W passes).
       pts[0]   = {0, h}      bottom-left corner  – skip (vertical edge)
       pts[1]   = {0, y_top}  top of left edge    – start here
       pts[n-2] = {w, y_top}  top of right edge   – include
       pts[n-1] = {w, h}      bottom-right corner – skip (vertical edge) */
    for (int pass = 0; pass < FOOTER_BORDER_W; pass++) {
        XSetForeground(dpy, gc, rs->col.footer_border);
        XPoint bpts[256];
        int nb = 0;
        for (int i = 1; i < n - 1; i++) {
            bpts[nb].x = pts[i].x;
            bpts[nb].y = pts[i].y + (short)pass;
            nb++;
        }
        XDrawLines(dpy, d, gc, bpts, nb, CoordModeOrigin);
    }
}

/* ── Icon loading (your original Imlib2 implementation) ─────────────────── */
static const char *icon_dirs[] = {
    "/usr/share/icons/Mint-Y/apps/96/",
    "/usr/share/icons/hicolor/128x128/apps",
    "/usr/share/icons/hicolor/64x64/apps",
    "/usr/share/icons/hicolor/48x48/apps",
    "/usr/share/pixmaps",
    NULL
};

static char *find_icon_file(const char *icon)
{
    static char path[512];

    if (!icon || !icon[0]) return NULL;

    if (icon[0] == '/') {
        if (access(icon, R_OK) == 0)
            return (char*)icon;
        return NULL;
    }

    const char *exts[] = { ".png", ".xpm", ".svg", NULL };

    for (int d = 0; icon_dirs[d]; d++) {
        for (int e = 0; exts[e]; e++) {
            snprintf(path, sizeof(path), "%s/%s%s",
                     icon_dirs[d], icon, exts[e]);
            if (access(path, R_OK) == 0)
                return path;
        }
    }

    return NULL;
}

/* Fill the full tile rectangle with the app icon (scaled to fit).
   ix,iy,iw,ih  = the tile's top-left and dimensions.
   corner_r     = the rounded-corner radius already used for the tile shape.
   The fallback fills the same rectangle with the coloured-initial block. */
static void draw_app_icon(RenderState *rs, Drawable d,
                           int ix, int iy, int iw, int ih, int corner_r,
                           const DesktopEntry *entry)
{
    Display   *dpy = rs->dpy;
    GC         gc  = rs->gc;
    WiiColors *c   = &rs->col;

    char *icon_path = find_icon_file(entry->icon);

    if (icon_path) {
        imlib_context_set_display(dpy);
        imlib_context_set_visual(DefaultVisual(dpy, rs->screen));
        imlib_context_set_colormap(DefaultColormap(dpy, rs->screen));
        imlib_context_set_drawable(d);

        Imlib_Image img = imlib_load_image(icon_path);
        if (img) {
            imlib_context_set_image(img);
            imlib_render_image_on_drawable_at_size(ix, iy, iw, ih);
            imlib_free_image();
            return;
        }
    }

    /* Fallback: solid colour fill + initial letter centred in tile */
    unsigned int hash = 5381;
    for (const char *p = entry->name; *p; p++)
        hash = ((hash << 5) + hash) ^ (unsigned char)*p;
    int r = 100 + ((hash       & 0x7F) * 80 / 127);
    int g = 100 + (((hash>>7)  & 0x7F) * 80 / 127);
    int b = 130 + (((hash>>14) & 0x7F) * 80 / 127);

    XSetForeground(dpy, gc, alloc_rgb(dpy, rs->screen, r, g, b));
    fill_rrect(dpy, d, gc, ix, iy, iw, ih, corner_r);

    /* Gloss highlight on upper third */
    XSetForeground(dpy, gc, alloc_rgb(dpy, rs->screen,
                                       (r+255)/2, (g+255)/2, (b+255)/2));
    fill_rrect(dpy, d, gc, ix+2, iy+2, iw-4, ih/3, corner_r);

    XSetFont(dpy, gc, rs->font_title->fid);
    XSetForeground(dpy, gc, c->white);
    int lw  = XTextWidth(rs->font_title, entry->name, 1);
    int asc = rs->font_title->ascent;
    XDrawString(dpy, d, gc,
                ix + iw/2 - lw/2 - (strlen(entry->name) * rs->font_title->max_bounds.width / 2),
                iy + ih/2 + asc/2,
                entry->name, strlen(entry->name));
}

/* ═══════════════════════════════════════════════════════════════════════════
   Main draw
   ═══════════════════════════════════════════════════════════════════════════ */

void renderer_draw(RenderState *rs)
{
    Display   *dpy  = rs->dpy;
    Drawable   bb   = rs->backbuf;
    GC         gc   = rs->gc;
    WiiColors *c    = &rs->col;
    AppList   *apps = rs->apps;

    /* ── Background ─────────────────────────────────────────────────────── */
    XSetForeground(dpy, gc, c->bg);
    XFillRectangle(dpy, bb, gc, 0, 0, rs->sw, rs->sh);

    /* ── Footer (draw first so tiles paint over the top edge) ───────────── */
    draw_footer(rs, bb, rs->sw, rs->sh);

    /* ── Channel tiles ──────────────────────────────────────────────────── */
    int focus_tx = 0, focus_ty = 0;

    for (int row = 0; row < ROWS; row++) {
        for (int col = 0; col < COLS; col++) {
            int app_row = rs->scroll_col + row;
            int idx = app_row * COLS + col;
            int tx  = rs->grid_x + col * (rs->tile_w + rs->tile_gap);
            int ty  = rs->grid_y + row * (rs->tile_h + rs->tile_gap)
                    - (rs->scroll_anim - rs->scroll_col*(rs->tile_h+rs->tile_gap));
            int focused = (idx == rs->focus_idx && idx < apps->count);

            if (focused) { focus_tx = tx; focus_ty = ty; }

            /* Drop shadow */
            for (int si = 4; si >= 1; si--) {
                int sv = 140 + si * 10;
                XSetForeground(dpy, gc,
                    alloc_rgb(dpy, rs->screen, sv, sv, sv+6));
                fill_rrect(dpy, bb, gc,
                           tx + si, ty + si + 2,
                           rs->tile_w, rs->tile_h, rs->tile_r);
            }

            if (idx >= apps->count) {
                XSetForeground(dpy, gc, alloc_rgb(dpy,rs->screen,220,222,226));
                fill_rrect(dpy, bb, gc, tx, ty, rs->tile_w, rs->tile_h, rs->tile_r);
                XSetForeground(dpy, gc, c->tile_border_norm);
                draw_rrect(dpy, bb, gc, tx, ty, rs->tile_w, rs->tile_h, rs->tile_r);
                continue;
            }

            DesktopEntry *entry = &apps->entries[idx];

            /* White base so rounded corners look clean with alpha icons */
            XSetForeground(dpy, gc, c->tile_face);
            fill_rrect(dpy, bb, gc, tx, ty, rs->tile_w, rs->tile_h, rs->tile_r);

            /* Icon fills the entire tile, scaled to fit */
            draw_app_icon(rs, bb,
                          tx, ty, rs->tile_w, rs->tile_h, rs->tile_r,
                          entry);

            /* Border / focus ring */
            if (focused) {
                double phase = rs->glow_phase / 255.0 * 2.0 * M_PI;
                double t = (sin(phase) + 1.0) / 2.0;
                int cr = (int)(70 + t * 30);
                int cg = (int)(180 + t * 30);
                int cb = (int)(220 + t * 25);
                for (int i = 3; i >= 0; i--) {
                    float ft = 1.0f - i * 0.25f;
                    XSetForeground(dpy, gc,
                        lerp_rgb(dpy, rs->screen,
                                 cr, cg, cb,
                                 210, 213, 218, ft));
                    draw_rrect(dpy, bb, gc,
                               tx - i, ty - i,
                               rs->tile_w + 2*i, rs->tile_h + 2*i, rs->tile_r+i);
                }
            } else {
                XSetForeground(dpy, gc, c->tile_border_norm);
                draw_rrect(dpy, bb, gc, tx, ty, rs->tile_w, rs->tile_h, rs->tile_r);
            }
        }
    }

    /* ── Tooltip below focused tile ─────────────────────────────────────── */
    DesktopEntry *fe = renderer_focused_entry(rs);
    if (fe)
        draw_tooltip(rs, bb, focus_tx, focus_ty, rs->tile_w, fe->name);

    /* ── Nav arrows ─────────────────────────────────────────────────────── */
    int total_rows = (apps->count + COLS - 1) / COLS;
    int grid_cy    = rs->grid_y + (ROWS * (rs->tile_h + rs->tile_gap)) / 2;

    if (rs->scroll_col > 0)
        draw_nav_arrow(rs, bb,
                       rs->grid_x - 52, grid_cy - 22,
                       44, 44, 1);
    if (rs->scroll_col + ROWS < total_rows)
        draw_nav_arrow(rs, bb,
                       rs->grid_x + COLS*(rs->tile_w+rs->tile_gap) - rs->tile_gap + 8,
                       grid_cy - 22,
                       44, 44, 0);

    /* ── Footer contents (your original placement) ──────────────────────── */
    int footer_top_y = rs->sh - rs->footer_h;
    int dip_depth    = (int)(rs->footer_h * 0.28f);

    /* ── Clock ─────────────────────────────────────────────────────────── */
    {
        time_t now = time(NULL);
        struct tm *tm = localtime(&now);

        char hbuf[8], mbuf[8], ampm[4];
        int hour12 = tm->tm_hour % 12;
        if (hour12 == 0) hour12 = 12;
        snprintf(hbuf, sizeof(hbuf), "%d", hour12);
        snprintf(mbuf, sizeof(mbuf), "%02d", tm->tm_min);
        snprintf(ampm, sizeof(ampm), "%s", tm->tm_hour >= 12 ? "PM" : "AM");

        /* Vertical baseline: footer_top + 28% dip - 5 (your original) */
        int clock_y = footer_top_y + dip_depth - 5;

        if (rs->xft_clock && rs->xft_draw) {
            /* ── TTF path via Xft ── */
            XftColor xft_col;
            XRenderColor rcol;
            /* clock_text colour → XRenderColor (16-bit per channel) */
            XColor tmp;
            tmp.pixel = c->clock_text;
            XQueryColor(dpy, DefaultColormap(dpy, rs->screen), &tmp);
            rcol.red   = tmp.red;
            rcol.green = tmp.green;
            rcol.blue  = tmp.blue;
            rcol.alpha = 0xFFFF;
            XftColorAllocValue(dpy, DefaultVisual(dpy, rs->screen),
                               DefaultColormap(dpy, rs->screen),
                               &rcol, &xft_col);

            XftFont *f = rs->xft_clock;

            /* Measure each segment */
            XGlyphInfo ext_h, ext_c, ext_m;
            XftTextExtentsUtf8(dpy, f,
                               (const FcChar8 *)hbuf, strlen(hbuf), &ext_h);
            XftTextExtentsUtf8(dpy, f,
                               (const FcChar8 *)":", 1, &ext_c);
            XftTextExtentsUtf8(dpy, f,
                               (const FcChar8 *)mbuf, strlen(mbuf), &ext_m);

            int total_w = ext_h.xOff + ext_c.xOff + ext_m.xOff;
            int cx      = rs->sw/2 - total_w/2;
            /* Xft draws from baseline; adjust so ascent sits above clock_y */
            int base_y  = clock_y;

            XftDrawStringUtf8(rs->xft_draw, &xft_col, f,
                              cx, base_y,
                              (const FcChar8 *)hbuf, strlen(hbuf));
            cx += ext_h.xOff;
            XftDrawStringUtf8(rs->xft_draw, &xft_col, f,
                              cx, base_y,
                              (const FcChar8 *)":", 1);
            cx += ext_c.xOff;
            XftDrawStringUtf8(rs->xft_draw, &xft_col, f,
                              cx, base_y,
                              (const FcChar8 *)mbuf, strlen(mbuf));

            /* AM/PM: use half the clock font size */
            {
                int ampm_px = f->height / 2;
                if (ampm_px < 10) ampm_px = 10;
                XftFont *fa = XftFontOpen(dpy, rs->screen,
                                  XFT_FILE,       XftTypeString,
                                      rs->ttf_clock_path,
                                  XFT_PIXEL_SIZE, XftTypeDouble,
                                      (double)ampm_px,
                                  XFT_ANTIALIAS,  XftTypeBool, True,
                                  NULL);
                if (fa) {
                    XGlyphInfo ea;
                    XftTextExtentsUtf8(dpy, fa,
                                       (const FcChar8 *)ampm, strlen(ampm), &ea);
                    (void)ea;
                    XftDrawStringUtf8(rs->xft_draw, &xft_col, fa,
                                      cx + ext_m.xOff + 6,
                                      base_y,
                                      (const FcChar8 *)ampm, strlen(ampm));
                    XftFontClose(dpy, fa);
                }
            }

            XftColorFree(dpy, DefaultVisual(dpy, rs->screen),
                         DefaultColormap(dpy, rs->screen), &xft_col);
        } else
        /* ── Xlib bitmap fallback ── */
        if (rs->font_clock) {
            XSetFont(dpy, gc, rs->font_clock->fid);
            XSetForeground(dpy, gc, c->clock_text);

            int hw = XTextWidth(rs->font_clock, hbuf, strlen(hbuf));
            int mw = XTextWidth(rs->font_clock, mbuf, strlen(mbuf));
            int cw = XTextWidth(rs->font_clock, ":", 1);

            int total_w = hw + cw + mw;
            int clock_x = rs->sw/2 - total_w/2;

            XDrawString(dpy, bb, gc, clock_x, clock_y,
                        hbuf, strlen(hbuf));
            XDrawString(dpy, bb, gc, clock_x + hw, clock_y,
                        ":", 1);
            XDrawString(dpy, bb, gc, clock_x + hw + cw, clock_y,
                        mbuf, strlen(mbuf));

            if (rs->font_clock_ampm) {
                XSetFont(dpy, gc, rs->font_clock_ampm->fid);
                XDrawString(dpy, bb, gc,
                            clock_x + total_w + 6, clock_y,
                            ampm, strlen(ampm));
            }
        }

        /* Date below clock – your original offset (+64) */
        if (rs->font_date) {
            const char *wdays[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
            char dbuf[32];
            snprintf(dbuf, sizeof(dbuf), "%s %d/%d",
                     wdays[tm->tm_wday], tm->tm_mon+1, tm->tm_mday);
            XSetFont(dpy, gc, rs->font_date->fid);
            XSetForeground(dpy, gc, c->clock_text);
            int dw = XTextWidth(rs->font_date, dbuf, strlen(dbuf));
            XDrawString(dpy, bb, gc,
                        rs->sw/2 - dw/2,
                        clock_y + 64,
                        dbuf, strlen(dbuf));
        }
    }

    /* ── Wii button (bottom-left) ── */
    draw_footer_btn(rs, bb,
                    80, rs->sh - 42, 34,
                    alloc_rgb(dpy, rs->screen, 180, 185, 200),
                    c->white,
                    "Wii");

    /* ── Mail button (bottom-right) ── */
    draw_footer_btn(rs, bb,
                    rs->sw - 80, rs->sh - 42, 34,
                    alloc_rgb(dpy, rs->screen, 180, 185, 200),
                    c->white,
                    "MSG");

    /* ── Scroll dots ── */
    if (total_rows > ROWS) {
        int ndots   = (total_rows + ROWS - 1) / ROWS;
        int cur_dot = rs->scroll_col / ROWS;
        int dot_x   = rs->sw/2 - (ndots * 14) / 2;
        int dot_y   = rs->sh - 12;
        for (int i = 0; i < ndots && i < 20; i++) {
            XSetForeground(dpy, gc,
                i == cur_dot ? c->tile_border_focus : c->text_light);
            XFillArc(dpy, bb, gc,
                     dot_x + i*14, dot_y, 8, 8, 0, 360*64);
        }
    }

    /* ── Blit ────────────────────────────────────────────────────────────── */
    XCopyArea(dpy, bb, rs->win, gc, 0, 0, rs->sw, rs->sh, 0, 0);
    XFlush(dpy);
}
