/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Panel system implementation
 */

#include "panel.h"
#include "workspace.h"
#include "layout.h"
#include "client.h"
#include "config.h"
#include "util.h"
#include "atoms.h"
#include "systray.h"
#include "news.h"

#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/statvfs.h>
#include <X11/Xft/Xft.h>

#define PANEL_PADDING 12
#define WIDGET_SPACING 16
#define WORKSPACE_WIDTH 36
#define TASKBAR_ITEM_WIDTH 180
#define TOP_PROCESS_NAME_MAX_LEN 12
#define TOP_PROCESS_WIDGET_WIDTH 150

#define CLOCK_FORMAT "%H:%M:%S"
#define DATE_FORMAT "%Y-%m-%d"

#define XFT_COLOR_CACHE_SIZE 16
#define PROCESS_SCAN_INTERVAL_MS 30000
#define PROC_CPU_HISTORY_SIZE 1024
#define DISK_UPDATE_INTERVAL_MS 60000

static char clock_buffer[32] = "";
static char date_buffer[32] = "";

static double disk_free_gb = 0.0;
static double disk_total_gb = 0.0;
static long disk_last_update = 0;

static bool panels_dirty = true;

static int news_region_x = 0;
static int news_region_width = 0;
static bool news_region_dirty = false;

typedef struct {
    unsigned long pixel;
    XftColor xft_color;
    bool valid;
    int lru_counter;
} CachedXftColor;

static CachedXftColor xft_color_cache[XFT_COLOR_CACHE_SIZE];
static Visual *cached_visual = NULL;
static int xft_color_lru_counter = 0;
static bool xft_cache_initialized = false;

static XftDraw *cached_xft_draw = NULL;
static Drawable cached_drawable = None;

static long last_memory_scan = 0;

typedef struct {
    int pid;
    unsigned long long prev_utime;
    unsigned long long prev_stime;
    unsigned long long prev_total_cpu;
} ProcCpuState;

typedef struct {
    int cpu_percent;
    int mem_percent;
    int mem_used_mb;
    int mem_total_mb;
    float load_1min;
    float load_5min;
    float load_15min;
    unsigned long long prev_idle;
    unsigned long long prev_total;
    char top_mem_processes[3][64];
    int top_mem_percents[3];
    int top_mem_count;
    char top_cpu_processes[3][64];
    int top_cpu_percents[3];
    int top_cpu_count;
    int process_display_index;
    int process_category;
    long process_last_cycle;
    bool process_show_cpu;
    long process_toggle_time;
} SystemStats;

static ProcCpuState proc_cpu_history[PROC_CPU_HISTORY_SIZE];
static int proc_cpu_history_count = 0;
static unsigned long long prev_total_cpu_time = 0;

static SystemStats sys_stats = {0};

static void panel_render_system_stats(Panel *panel, int x, int *width);
static int panel_calculate_stats_width(void);
static void panel_render_top_process(Panel *panel, int x, int *width);
static void panel_render_key_counter(Panel *panel, int x, int *width);
static void panel_render_mouse_distance(Panel *panel, int x, int *width);
static void panel_render_disk_space(Panel *panel, int x, int *width);
static void update_disk_space(void);
static void update_top_processes(void);


static int panel_text_width(const char *text, int len)
{
    if (text == NULL || dwn == NULL) return 0;

    if (dwn->xft_font != NULL) {
        XGlyphInfo extents;
        XftTextExtentsUtf8(dwn->display, dwn->xft_font,
                           (const FcChar8 *)text, len, &extents);
        return extents.xOff;
    }

    if (dwn->font != NULL) {
        return XTextWidth(dwn->font, text, len);
    }

    return 0;
}

static XftDraw *panel_get_xft_draw(Drawable d)
{
    if (cached_drawable == d && cached_xft_draw != NULL) {
        return cached_xft_draw;
    }
    if (cached_xft_draw != NULL) {
        XftDrawDestroy(cached_xft_draw);
    }
    if (cached_visual == NULL) {
        cached_visual = DefaultVisual(dwn->display, dwn->screen);
    }
    cached_xft_draw = XftDrawCreate(dwn->display, d, cached_visual, dwn->colormap);
    cached_drawable = d;
    return cached_xft_draw;
}

static XftColor *panel_get_cached_xft_color(unsigned long color)
{
    if (cached_visual == NULL) {
        cached_visual = DefaultVisual(dwn->display, dwn->screen);
    }

    /* Initialize cache on first use */
    if (!xft_cache_initialized) {
        memset(xft_color_cache, 0, sizeof(xft_color_cache));
        xft_cache_initialized = true;
    }

    for (int i = 0; i < XFT_COLOR_CACHE_SIZE; i++) {
        if (xft_color_cache[i].valid && xft_color_cache[i].pixel == color) {
            xft_color_cache[i].lru_counter = ++xft_color_lru_counter;
            return &xft_color_cache[i].xft_color;
        }
    }

    int oldest_idx = 0;
    int oldest_lru = xft_color_cache[0].lru_counter;
    bool found_invalid = false;
    
    for (int i = 0; i < XFT_COLOR_CACHE_SIZE; i++) {
        if (!xft_color_cache[i].valid) {
            oldest_idx = i;
            found_invalid = true;
            break;
        }
    }
    
    if (!found_invalid) {
        for (int i = 1; i < XFT_COLOR_CACHE_SIZE; i++) {
            if (xft_color_cache[i].lru_counter < oldest_lru) {
                oldest_lru = xft_color_cache[i].lru_counter;
                oldest_idx = i;
            }
        }
    }

    if (xft_color_cache[oldest_idx].valid) {
        XftColorFree(dwn->display, cached_visual, dwn->colormap,
                     &xft_color_cache[oldest_idx].xft_color);
    }

    XRenderColor render_color;
    render_color.red = ((color >> 16) & 0xFF) * 257;
    render_color.green = ((color >> 8) & 0xFF) * 257;
    render_color.blue = (color & 0xFF) * 257;
    render_color.alpha = 0xFFFF;

    XftColorAllocValue(dwn->display, cached_visual, dwn->colormap,
                       &render_color, &xft_color_cache[oldest_idx].xft_color);
    xft_color_cache[oldest_idx].pixel = color;
    xft_color_cache[oldest_idx].valid = true;
    xft_color_cache[oldest_idx].lru_counter = ++xft_color_lru_counter;

    return &xft_color_cache[oldest_idx].xft_color;
}

static void panel_draw_text_with_font(Drawable d, int x, int y, const char *text,
                                       int len, unsigned long color, XftFont *font)
{
    if (text == NULL || dwn == NULL || dwn->display == NULL) return;

    if (font != NULL) {
        XftDraw *xft_draw = panel_get_xft_draw(d);
        if (xft_draw != NULL) {
            XftColor *xft_color = panel_get_cached_xft_color(color);
            XftDrawStringUtf8(xft_draw, xft_color, font,
                              x, y, (const FcChar8 *)text, len);
            return;
        }
    }

    XSetForeground(dwn->display, dwn->gc, color);
    XDrawString(dwn->display, d, dwn->gc, x, y, text, len);
}

static void panel_draw_text(Drawable d, int x, int y, const char *text,
                            int len, unsigned long color)
{
    panel_draw_text_with_font(d, x, y, text, len, color, dwn->xft_font);
}

static void panel_draw_text_bold(Drawable d, int x, int y, const char *text,
                                  int len, unsigned long color)
{
    XftFont *font = dwn->xft_font_bold ? dwn->xft_font_bold : dwn->xft_font;
    panel_draw_text_with_font(d, x, y, text, len, color, font);
}

static int panel_text_y(int panel_height)
{
    if (dwn->xft_font != NULL) {
        return (panel_height + dwn->xft_font->ascent) / 2;
    }
    if (dwn->font != NULL) {
        return (panel_height + dwn->font->ascent - dwn->font->descent) / 2;
    }
    return panel_height / 2;
}


Panel *panel_create(PanelPosition position)
{
    if (dwn == NULL || dwn->display == NULL) {
        return NULL;
    }

    Panel *panel = dwn_calloc(1, sizeof(Panel));
    panel->position = position;
    panel->width = dwn->screen_width;
    panel->height = config_get_panel_height();
    panel->x = 0;
    panel->visible = true;

    if (position == PANEL_TOP) {
        panel->y = 0;
    } else {
        panel->y = dwn->screen_height - panel->height;
    }

    XSetWindowAttributes swa;
    swa.override_redirect = True;
    swa.background_pixel = dwn->config->colors.panel_bg;
    swa.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;

    panel->window = XCreateWindow(dwn->display, dwn->root,
                                  panel->x, panel->y,
                                  panel->width, panel->height,
                                  0,
                                  CopyFromParent, InputOutput, CopyFromParent,
                                  CWOverrideRedirect | CWBackPixel | CWEventMask,
                                  &swa);

    panel->buffer = XCreatePixmap(dwn->display, panel->window,
                                  panel->width, panel->height,
                                  DefaultDepth(dwn->display, dwn->screen));

    long strut[4] = { 0, 0, 0, 0 };
    if (position == PANEL_TOP) {
        strut[2] = panel->height;
    } else {
        strut[3] = panel->height;
    }

    XChangeProperty(dwn->display, panel->window, ewmh.NET_WM_STRUT,
                    XA_CARDINAL, 32, PropModeReplace,
                    (unsigned char *)strut, 4);

    long strut_partial[12] = { 0 };
    if (position == PANEL_TOP) {
        strut_partial[2] = panel->height;
        strut_partial[8] = 0;
        strut_partial[9] = dwn->screen_width - 1;
    } else {
        strut_partial[3] = panel->height;
        strut_partial[10] = 0;
        strut_partial[11] = dwn->screen_width - 1;
    }

    XChangeProperty(dwn->display, panel->window, ewmh.NET_WM_STRUT_PARTIAL,
                    XA_CARDINAL, 32, PropModeReplace,
                    (unsigned char *)strut_partial, 12);

    XChangeProperty(dwn->display, panel->window, ewmh.NET_WM_WINDOW_TYPE,
                    XA_ATOM, 32, PropModeReplace,
                    (unsigned char *)&ewmh.NET_WM_WINDOW_TYPE_DOCK, 1);

    LOG_DEBUG("Created %s panel at y=%d",
              position == PANEL_TOP ? "top" : "bottom", panel->y);

    return panel;
}

void panel_destroy(Panel *panel)
{
    if (panel == NULL) {
        return;
    }

    if (panel->buffer != None) {
        XFreePixmap(dwn->display, panel->buffer);
    }

    if (panel->window != None) {
        XDestroyWindow(dwn->display, panel->window);
    }

    dwn_free(panel);
}

void panels_init(void)
{
    if (dwn == NULL || dwn->config == NULL) {
        return;
    }

    if (dwn->config->top_panel_enabled) {
        dwn->top_panel = panel_create(PANEL_TOP);
        if (dwn->top_panel != NULL) {
            XMapRaised(dwn->display, dwn->top_panel->window);
        }
    }

    if (dwn->config->bottom_panel_enabled) {
        dwn->bottom_panel = panel_create(PANEL_BOTTOM);
        if (dwn->bottom_panel != NULL) {
            XMapRaised(dwn->display, dwn->bottom_panel->window);
        }
    }

    panel_update_clock();
    panel_update_system_stats();

    LOG_INFO("Panels initialized");
}

void panels_cleanup(void)
{
    if (dwn->top_panel != NULL) {
        panel_destroy(dwn->top_panel);
        dwn->top_panel = NULL;
    }

    if (dwn->bottom_panel != NULL) {
        panel_destroy(dwn->bottom_panel);
        dwn->bottom_panel = NULL;
    }
}


void panel_render(Panel *panel)
{
    if (panel == NULL || !panel->visible) {
        return;
    }

    Display *dpy = dwn->display;
    const ColorScheme *colors = config_get_colors();

    unsigned long bg = ambient_glow_bg(colors->panel_bg, dwn->ambient_phase + PHASE_OFFSET_PANEL_BG);
    XSetForeground(dpy, dwn->gc, bg);
    XFillRectangle(dpy, panel->buffer, dwn->gc, 0, 0, panel->width, panel->height);

    int x = PANEL_PADDING;
    int width;

    if (panel->position == PANEL_TOP) {

        panel_render_workspaces(panel, x, &width);
        x += width + WIDGET_SPACING;

        panel_render_layout_indicator(panel, x, &width);
        x += width + WIDGET_SPACING;

        panel_render_taskbar(panel, x, &width);

        int systray_actual_width = systray_get_width();
        int systray_x = panel->width - systray_actual_width - PANEL_PADDING;
        
        /* Render fade controls before systray */
        int fade_width = fade_controls_get_width();
        int fade_x = systray_x - fade_width - WIDGET_SPACING;
        fade_controls_render(panel, fade_x);
        
        systray_render(panel, systray_x, &width);

        if (dwn->ai_enabled) {
            int ai_x = systray_x - 60;
            panel_render_ai_status(panel, ai_x, &width);
        }
    } else {

        int date_width = 0;
        if (dwn->xft_font != NULL || dwn->font != NULL) {
            int text_y = panel_text_y(panel->height);
            unsigned long date_fg = ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_CLOCK);
            panel_draw_text(panel->buffer, x, text_y, date_buffer,
                            strlen(date_buffer), date_fg);
            date_width = panel_text_width(date_buffer, strlen(date_buffer));
        }

        int top_proc_width = 0;
        int top_proc_x = PANEL_PADDING + date_width + WIDGET_SPACING;
        panel_render_top_process(panel, top_proc_x, &top_proc_width);

        int key_counter_width = 0;
        int key_counter_x = top_proc_x + top_proc_width + WIDGET_SPACING;
        panel_render_key_counter(panel, key_counter_x, &key_counter_width);

        int mouse_dist_width = 0;
        int mouse_dist_x = key_counter_x + key_counter_width + WIDGET_SPACING;
        panel_render_mouse_distance(panel, mouse_dist_x, &mouse_dist_width);

        int disk_space_width = 0;
        int disk_space_x = mouse_dist_x + mouse_dist_width + WIDGET_SPACING;
        panel_render_disk_space(panel, disk_space_x, &disk_space_width);

        int clock_width = panel_text_width(clock_buffer, strlen(clock_buffer));
        int stats_width = panel_calculate_stats_width();

        int clock_x = panel->width - clock_width - PANEL_PADDING;
        panel_render_clock(panel, clock_x, &width);

        int stats_x = clock_x - stats_width - WIDGET_SPACING;
        panel_render_system_stats(panel, stats_x, &width);

        int news_start = disk_space_x + disk_space_width + WIDGET_SPACING;
        int news_max_width = stats_x - news_start - WIDGET_SPACING;
        if (news_max_width > 100) {
            panel_set_news_region(news_start, news_max_width);
            int news_width = 0;
            news_render(panel, news_start, news_max_width, &news_width);
        }
    }

    XCopyArea(dpy, panel->buffer, panel->window, dwn->gc,
              0, 0, panel->width, panel->height, 0, 0);
}

void panel_render_all(void)
{
    if (dwn->top_panel != NULL) {
        panel_render(dwn->top_panel);
        dwn->top_panel->dirty = false;
    }
    if (dwn->bottom_panel != NULL) {
        panel_render(dwn->bottom_panel);
        dwn->bottom_panel->dirty = false;
    }
    panels_dirty = false;
    XFlush(dwn->display);
}

void panel_render_workspaces(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        return;
    }

    Display *dpy = dwn->display;
    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    *width = 0;

    for (int i = 0; i < MAX_WORKSPACES; i++) {
        bool active = (i == dwn->current_workspace);
        bool has_clients = !workspace_is_empty(i);

        unsigned long bg_color = active ?
            ambient_glow_accent(colors->workspace_active, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES) :
            ambient_glow_bg(colors->panel_bg, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES);
        XSetForeground(dpy, dwn->gc, bg_color);
        XFillRectangle(dpy, panel->buffer, dwn->gc,
                       x + i * WORKSPACE_WIDTH, 0,
                       WORKSPACE_WIDTH, panel->height);

        char num[4];
        snprintf(num, sizeof(num), "%d", i + 1);

        unsigned long fg = active ? colors->panel_bg :
            ambient_glow_bg(has_clients ? colors->panel_fg : colors->workspace_inactive, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES);

        int text_x = x + i * WORKSPACE_WIDTH + (WORKSPACE_WIDTH - panel_text_width(num, strlen(num))) / 2;
        panel_draw_text(panel->buffer, text_x, text_y, num, strlen(num), fg);
    }

    *width = MAX_WORKSPACES * WORKSPACE_WIDTH;
}

void panel_render_taskbar(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        return;
    }

    Display *dpy = dwn->display;
    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    int current_x = x;
    int systray_actual_width = systray_get_width();
    int ai_width = dwn->ai_enabled ? 60 : 0;
    int right_reserve = systray_actual_width + ai_width + PANEL_PADDING * 2;
    int available_width = panel->width - x - right_reserve;
    if (available_width < 0) available_width = 0;
    int item_count = 0;

    Workspace *ws = workspace_get_current();
    if (ws == NULL) {
        *width = 0;
        return;
    }

    for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
        item_count++;
    }

    if (item_count == 0) {
        *width = 0;
        return;
    }

    int item_width = available_width / item_count;
    if (item_width > TASKBAR_ITEM_WIDTH) {
        item_width = TASKBAR_ITEM_WIDTH;
    }

    for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
        if (c->workspace != (unsigned int)dwn->current_workspace) {
            continue;
        }

        bool focused = (ws->focused == c);
        bool minimized = client_is_minimized(c);

        unsigned long bg;
        if (minimized) {
            bg = colors->workspace_inactive;
        } else {
            bg = colors->panel_bg;
        }
        XSetForeground(dpy, dwn->gc, bg);
        XFillRectangle(dpy, panel->buffer, dwn->gc,
                       current_x, 0, item_width, panel->height);

        char title[260];
        if (minimized) {
            snprintf(title, sizeof(title), "[%s]", c->title);
        } else {
            snprintf(title, sizeof(title), "%s", c->title);
        }

        int max_text_width = item_width - 8;
        bool truncated = false;
        size_t title_len = strlen(title);
        while (panel_text_width(title, (int)title_len) > max_text_width && title_len > 3) {
            size_t cut = title_len - 1;
            while (cut > 0 && (title[cut] & 0xC0) == 0x80) {
                cut--;
            }
            if (cut > 0) cut--;
            while (cut > 0 && (title[cut] & 0xC0) == 0x80) {
                cut--;
            }
            if (cut > sizeof(title) - 4) {
                cut = sizeof(title) - 4;
            }
            title[cut] = '\0';
            title_len = cut;
            truncated = true;
        }
        if (truncated) {
            strncat(title, "...", sizeof(title) - title_len - 1);
            title_len += 3;
        }

        unsigned long fg;
        if (minimized) {
            fg = ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_TASKBAR);
        } else {
            fg = client_get_glow_text_color(c);
        }

        if (focused && !minimized) {
            panel_draw_text_bold(panel->buffer, current_x + 4, text_y, title, strlen(title), fg);
        } else {
            panel_draw_text(panel->buffer, current_x + 4, text_y, title, strlen(title), fg);
        }

        current_x += item_width;
    }

    *width = current_x - x;
}

void panel_render_clock(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        return;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    unsigned long fg = ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_CLOCK);
    panel_draw_text(panel->buffer, x, text_y, clock_buffer,
                    strlen(clock_buffer), fg);

    *width = panel_text_width(clock_buffer, strlen(clock_buffer));
}

void panel_render_systray(Panel *panel, int x, int *width)
{
    (void)panel;
    (void)x;
    *width = 0;
}

void panel_render_layout_indicator(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        return;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    Workspace *ws = workspace_get_current();
    const char *symbol = layout_get_symbol(ws != NULL ? ws->layout : LAYOUT_TILING);

    unsigned long fg = ambient_glow_accent(colors->workspace_active, dwn->ambient_phase + PHASE_OFFSET_WORKSPACES);
    panel_draw_text(panel->buffer, x, text_y, symbol, strlen(symbol), fg);

    *width = panel_text_width(symbol, strlen(symbol));
}

void panel_render_ai_status(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        return;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    const char *status = dwn->ai_enabled ? "[AI]" : "";

    unsigned long fg = ambient_glow_accent(colors->workspace_active, dwn->ambient_phase + PHASE_OFFSET_SYSTRAY);
    panel_draw_text(panel->buffer, x, text_y, status, strlen(status), fg);

    *width = panel_text_width(status, strlen(status));
}


void panel_handle_click(Panel *panel, int x, int y, int button)
{
    if (panel == NULL) {
        return;
    }

    if (panel->position == PANEL_TOP) {
        /* Check fade controls first */
        int fade_hit = fade_controls_hit_test(x);
        LOG_DEBUG("Panel click: x=%d, fade_hit=%d", x, fade_hit);
        if (fade_hit > 0) {
            LOG_DEBUG("Fade control clicked: control_id=%d", fade_hit);
            fade_controls_handle_click(fade_hit, x, y, button);
            return;
        }
        
        int systray_actual_width = systray_get_width();
        int systray_start = panel->width - systray_actual_width - PANEL_PADDING;
        
        if (x >= systray_start) {
            systray_handle_click(x, y, button);
            return;
        }

        int ws = panel_hit_test_workspace(panel, x, y);
        if (ws >= 0 && ws < MAX_WORKSPACES) {
            if (button == 1) {
                workspace_switch(ws);
            }
            return;
        }

        Client *c = panel_hit_test_taskbar(panel, x, y);
        if (c != NULL) {
            if (button == 1) {
                if (client_is_minimized(c)) {
                    client_restore(c);
                } else {
                    Workspace *current = workspace_get(dwn->current_workspace);
                    if (current != NULL && current->focused == c) {
                        client_minimize(c);
                    } else {
                        client_focus(c, true);
                    }
                }
            } else if (button == 3) {
                client_close(c);
            }
            return;
        }
    } else if (panel->position == PANEL_BOTTOM) {
        if (button == 1) {
            news_handle_click(x, y);
        }
    }
}

int panel_hit_test_workspace(Panel *panel, int x, int y)
{
    (void)y;

    if (panel == NULL || panel->position != PANEL_TOP) {
        return -1;
    }

    if (x < PANEL_PADDING || x >= PANEL_PADDING + MAX_WORKSPACES * WORKSPACE_WIDTH) {
        return -1;
    }

    return (x - PANEL_PADDING) / WORKSPACE_WIDTH;
}

Client *panel_hit_test_taskbar(Panel *panel, int x, int y)
{
    (void)y;

    if (panel == NULL || panel->position != PANEL_TOP) {
        return NULL;
    }

    Workspace *ws = workspace_get_current();
    if (ws == NULL) {
        return NULL;
    }

    const char *layout_symbol = layout_get_symbol(ws->layout);
    int layout_width = panel_text_width(layout_symbol, strlen(layout_symbol));
    int taskbar_start = PANEL_PADDING + MAX_WORKSPACES * WORKSPACE_WIDTH + WIDGET_SPACING + layout_width + WIDGET_SPACING;
    if (x < taskbar_start) {
        return NULL;
    }

    int systray_actual_width = systray_get_width();
    int ai_width = dwn->ai_enabled ? 60 : 0;
    int right_reserve = systray_actual_width + ai_width + PANEL_PADDING * 2;
    int available_width = panel->width - taskbar_start - right_reserve;
    if (available_width < 0) available_width = 0;
    int item_count = 0;

    for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
        item_count++;
    }

    if (item_count == 0) {
        return NULL;
    }

    int item_width = available_width / item_count;
    if (item_width > TASKBAR_ITEM_WIDTH) {
        item_width = TASKBAR_ITEM_WIDTH;
    }

    int index = (x - taskbar_start) / item_width;
    int i = 0;

    for (Client *c = ws->mru_head; c != NULL; c = c->mru_next) {
        if (i == index) {
            return c;
        }
        i++;
    }

    return NULL;
}


void panel_show(Panel *panel)
{
    if (panel == NULL) {
        return;
    }

    panel->visible = true;
    XMapRaised(dwn->display, panel->window);
    panel_render(panel);
}

void panel_hide(Panel *panel)
{
    if (panel == NULL) {
        return;
    }

    panel->visible = false;
    XUnmapWindow(dwn->display, panel->window);
}

void panel_toggle(Panel *panel)
{
    if (panel == NULL) {
        return;
    }

    if (panel->visible) {
        panel_hide(panel);
    } else {
        panel_show(panel);
    }
}

void panel_raise_all(void)
{
    if (dwn == NULL || dwn->display == NULL) {
        return;
    }

    if (dwn->top_panel != NULL && dwn->top_panel->visible) {
        XRaiseWindow(dwn->display, dwn->top_panel->window);
    }
    if (dwn->bottom_panel != NULL && dwn->bottom_panel->visible) {
        XRaiseWindow(dwn->display, dwn->bottom_panel->window);
    }
}

void panel_update_clock(void)
{
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);

    strftime(clock_buffer, sizeof(clock_buffer), CLOCK_FORMAT, tm_info);
    strftime(date_buffer, sizeof(date_buffer), DATE_FORMAT, tm_info);
}


void panel_update_system_stats(void)
{
    FILE *fp;
    char line[256];

    fp = fopen("/proc/stat", "r");
    if (fp != NULL) {
        if (fgets(line, sizeof(line), fp) != NULL) {
            unsigned long long user, nice, system, idle, iowait, irq, softirq;
            if (sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu",
                       &user, &nice, &system, &idle, &iowait, &irq, &softirq) >= 4) {
                unsigned long long total = user + nice + system + idle + iowait + irq + softirq;
                unsigned long long idle_time = idle + iowait;

                if (sys_stats.prev_total > 0) {
                    unsigned long long total_diff = total - sys_stats.prev_total;
                    unsigned long long idle_diff = idle_time - sys_stats.prev_idle;
                    if (total_diff > 0) {
                        sys_stats.cpu_percent = (int)(100 * (total_diff - idle_diff) / total_diff);
                    }
                }

                sys_stats.prev_total = total;
                sys_stats.prev_idle = idle_time;
            }
        }
        fclose(fp);
    }

    fp = fopen("/proc/meminfo", "r");
    if (fp != NULL) {
        unsigned long mem_total = 0, mem_free = 0, buffers = 0, cached = 0;

        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strncmp(line, "MemTotal:", 9) == 0) {
                sscanf(line + 9, " %lu", &mem_total);
            } else if (strncmp(line, "MemFree:", 8) == 0) {
                sscanf(line + 8, " %lu", &mem_free);
            } else if (strncmp(line, "Buffers:", 8) == 0) {
                sscanf(line + 8, " %lu", &buffers);
            } else if (strncmp(line, "Cached:", 7) == 0) {
                sscanf(line + 7, " %lu", &cached);
                break;
            }
        }
        fclose(fp);

        if (mem_total > 0) {
            unsigned long used = mem_total - mem_free - buffers - cached;
            sys_stats.mem_total_mb = (int)(mem_total / 1024);
            sys_stats.mem_used_mb = (int)(used / 1024);
            sys_stats.mem_percent = (int)(100 * used / mem_total);
        }
    }

    fp = fopen("/proc/loadavg", "r");
    if (fp != NULL) {
        if (fscanf(fp, "%f %f %f",
                   &sys_stats.load_1min,
                   &sys_stats.load_5min,
                   &sys_stats.load_15min) != 3) {
            sys_stats.load_1min = 0;
            sys_stats.load_5min = 0;
            sys_stats.load_15min = 0;
        }
        fclose(fp);
    }

    long now = get_time_ms();
    if (now - last_memory_scan >= PROCESS_SCAN_INTERVAL_MS) {
        update_top_processes();
        last_memory_scan = now;
    }

    static int prev_cpu = -1;
    static int prev_mem = -1;
    bool stats_changed = (prev_cpu != sys_stats.cpu_percent) ||
                         (prev_mem != sys_stats.mem_percent);
    if (stats_changed) {
        prev_cpu = sys_stats.cpu_percent;
        prev_mem = sys_stats.mem_percent;
        panel_invalidate();
    }
}

static ProcCpuState *find_cpu_history(int pid)
{
    for (int i = 0; i < proc_cpu_history_count; i++) {
        if (proc_cpu_history[i].pid == pid) {
            return &proc_cpu_history[i];
        }
    }
    return NULL;
}

static ProcCpuState *add_cpu_history(int pid)
{
    if (proc_cpu_history_count < PROC_CPU_HISTORY_SIZE) {
        ProcCpuState *state = &proc_cpu_history[proc_cpu_history_count++];
        state->pid = pid;
        state->prev_utime = 0;
        state->prev_stime = 0;
        state->prev_total_cpu = 0;
        return state;
    }

    char path[64];
    for (int i = 0; i < PROC_CPU_HISTORY_SIZE; i++) {
        snprintf(path, sizeof(path), "/proc/%d", proc_cpu_history[i].pid);
        if (access(path, F_OK) != 0) {
            proc_cpu_history[i].pid = pid;
            proc_cpu_history[i].prev_utime = 0;
            proc_cpu_history[i].prev_stime = 0;
            proc_cpu_history[i].prev_total_cpu = 0;
            return &proc_cpu_history[i];
        }
    }

    proc_cpu_history[0].pid = pid;
    proc_cpu_history[0].prev_utime = 0;
    proc_cpu_history[0].prev_stime = 0;
    proc_cpu_history[0].prev_total_cpu = 0;
    return &proc_cpu_history[0];
}

static unsigned long long read_total_cpu_time(void)
{
    FILE *fp = fopen("/proc/stat", "r");
    if (fp == NULL) return 0;

    char line[256];
    unsigned long long total = 0;
    if (fgets(line, sizeof(line), fp) != NULL) {
        unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;
        if (sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
                   &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal) >= 4) {
            total = user + nice + system + idle + iowait + irq + softirq + steal;
        }
    }
    fclose(fp);
    return total;
}

static void update_top_processes(void)
{
    DIR *proc_dir = opendir("/proc");
    if (proc_dir == NULL) {
        sys_stats.top_mem_count = 0;
        sys_stats.top_cpu_count = 0;
        return;
    }

    unsigned long long current_total_cpu = read_total_cpu_time();
    unsigned long long cpu_delta = current_total_cpu - prev_total_cpu_time;

    unsigned long top_rss[3] = {0, 0, 0};
    char top_mem_names[3][64] = {"", "", ""};
    unsigned long mem_total_kb = (unsigned long)sys_stats.mem_total_mb * 1024;

    int top_cpu_pcts[3] = {0, 0, 0};
    char top_cpu_names[3][64] = {"", "", ""};

    struct dirent *entry;
    while ((entry = readdir(proc_dir)) != NULL) {
        bool is_pid = true;
        size_t name_len = 0;
        for (int i = 0; entry->d_name[i] != '\0'; i++) {
            if (!isdigit((unsigned char)entry->d_name[i])) {
                is_pid = false;
                break;
            }
            name_len++;
        }
        if (!is_pid || name_len > 20) continue;

        int pid = atoi(entry->d_name);

        char status_path[64];
        snprintf(status_path, sizeof(status_path), "/proc/%d/status", pid);

        FILE *fp = fopen(status_path, "r");
        if (fp == NULL) continue;

        char line[256];
        char name[64] = "";
        unsigned long rss_kb = 0;

        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strncmp(line, "Name:", 5) == 0) {
                char *start = line + 5;
                while (*start == ' ' || *start == '\t') start++;
                size_t len = strlen(start);
                if (len > 0 && start[len - 1] == '\n') start[len - 1] = '\0';
                snprintf(name, sizeof(name), "%s", start);
            } else if (strncmp(line, "VmRSS:", 6) == 0) {
                sscanf(line + 6, " %lu", &rss_kb);
                break;
            }
        }
        fclose(fp);

        if (rss_kb > 0 && name[0] != '\0') {
            for (int i = 0; i < 3; i++) {
                if (rss_kb > top_rss[i]) {
                    for (int j = 2; j > i; j--) {
                        top_rss[j] = top_rss[j - 1];
                        memcpy(top_mem_names[j], top_mem_names[j - 1], 64);
                    }
                    top_rss[i] = rss_kb;
                    memcpy(top_mem_names[i], name, 64);
                    break;
                }
            }
        }

        char stat_path[64];
        snprintf(stat_path, sizeof(stat_path), "/proc/%d/stat", pid);
        fp = fopen(stat_path, "r");
        if (fp == NULL) continue;

        unsigned long long utime = 0, stime = 0;
        char stat_line[512];
        if (fgets(stat_line, sizeof(stat_line), fp) != NULL) {
            char *close_paren = strrchr(stat_line, ')');
            if (close_paren != NULL) {
                if (sscanf(close_paren + 2, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu",
                           &utime, &stime) == 2) {
                    ProcCpuState *hist = find_cpu_history(pid);
                    if (hist == NULL) {
                        hist = add_cpu_history(pid);
                    }

                    if (hist->prev_total_cpu > 0 && cpu_delta > 0) {
                        unsigned long long proc_delta = (utime + stime) - (hist->prev_utime + hist->prev_stime);
                        int num_cpus = (int)sysconf(_SC_NPROCESSORS_ONLN);
                        if (num_cpus < 1) num_cpus = 1;
                        int cpu_pct = (int)((proc_delta * 100 * (unsigned long long)num_cpus) / cpu_delta);
                        if (cpu_pct > 100) cpu_pct = 100;

                        for (int i = 0; i < 3; i++) {
                            if (cpu_pct > top_cpu_pcts[i] || top_cpu_names[i][0] == '\0') {
                                if (cpu_pct > top_cpu_pcts[i]) {
                                    for (int j = 2; j > i; j--) {
                                        top_cpu_pcts[j] = top_cpu_pcts[j - 1];
                                        memcpy(top_cpu_names[j], top_cpu_names[j - 1], 64);
                                    }
                                }
                                top_cpu_pcts[i] = cpu_pct;
                                memcpy(top_cpu_names[i], name, 64);
                                break;
                            }
                        }
                    }

                    hist->prev_utime = utime;
                    hist->prev_stime = stime;
                    hist->prev_total_cpu = current_total_cpu;
                }
            }
        }
        fclose(fp);
    }
    closedir(proc_dir);

    prev_total_cpu_time = current_total_cpu;

    sys_stats.top_mem_count = 0;
    for (int i = 0; i < 3; i++) {
        if (top_mem_names[i][0] != '\0') {
            memcpy(sys_stats.top_mem_processes[i], top_mem_names[i], 64);
            if (mem_total_kb > 0) {
                sys_stats.top_mem_percents[i] = (int)((top_rss[i] * 100) / mem_total_kb);
            } else {
                sys_stats.top_mem_percents[i] = 0;
            }
            sys_stats.top_mem_count++;
        }
    }

    sys_stats.top_cpu_count = 0;
    for (int i = 0; i < 3; i++) {
        if (top_cpu_names[i][0] != '\0') {
            memcpy(sys_stats.top_cpu_processes[i], top_cpu_names[i], 64);
            sys_stats.top_cpu_percents[i] = top_cpu_pcts[i];
            sys_stats.top_cpu_count++;
        }
    }
}

static int panel_calculate_stats_width(void)
{
    if (dwn->xft_font == NULL && dwn->font == NULL) return 0;

    char buf[256];
    int total = 0;

    int len = snprintf(buf, sizeof(buf), "CPU:%2d%%", sys_stats.cpu_percent);
    total += panel_text_width(buf, len) + WIDGET_SPACING;

    if (sys_stats.mem_total_mb >= 1024) {
        len = snprintf(buf, sizeof(buf), "MEM:%.1fG/%dG",
                       sys_stats.mem_used_mb / 1024.0f, sys_stats.mem_total_mb / 1024);
    } else {
        len = snprintf(buf, sizeof(buf), "MEM:%dM/%dM",
                       sys_stats.mem_used_mb, sys_stats.mem_total_mb);
    }
    total += panel_text_width(buf, len) + WIDGET_SPACING;

    len = snprintf(buf, sizeof(buf), "Load:%.2f %.2f %.2f",
                   sys_stats.load_1min, sys_stats.load_5min, sys_stats.load_15min);
    total += panel_text_width(buf, len) + WIDGET_SPACING;

    MultiBatteryState multi_bat = systray_get_multi_battery_snapshot();
    if (multi_bat.count > 0) {
        const char *bat_icon = multi_bat.any_charging ? "[+]" : "[=]";
        if (multi_bat.count == 1) {
            len = snprintf(buf, sizeof(buf), "%s%d%%", bat_icon, multi_bat.combined_percentage);
        } else {
            int offset = snprintf(buf, sizeof(buf), "%s%d%% (", bat_icon, multi_bat.combined_percentage);
            for (int i = 0; i < multi_bat.count && offset < (int)sizeof(buf) - 10; i++) {
                if (i > 0) {
                    offset += snprintf(buf + offset, sizeof(buf) - offset, " ");
                }
                offset += snprintf(buf + offset, sizeof(buf) - offset, "B%d:%d%%",
                                   i + 1, multi_bat.batteries[i].percentage);
            }
            snprintf(buf + offset, sizeof(buf) - offset, ")");
            len = strlen(buf);
        }
        total += panel_text_width(buf, len) + WIDGET_SPACING;
    }

    return total;
}

static void panel_render_system_stats(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        *width = 0;
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        *width = 0;
        return;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);
    int current_x = x;


    char stats_buf[256];
    int len;

    unsigned long cpu_color = colors->panel_fg;
    if (sys_stats.cpu_percent >= 90) {
        cpu_color = colors->workspace_urgent;
    } else if (sys_stats.cpu_percent >= 70) {
        cpu_color = 0xFFA500;
    }
    cpu_color = ambient_glow_bg(cpu_color, dwn->ambient_phase + PHASE_OFFSET_STATS);

    len = snprintf(stats_buf, sizeof(stats_buf), "CPU:%2d%%", sys_stats.cpu_percent);
    panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, cpu_color);
    current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING;

    unsigned long mem_color = colors->panel_fg;
    if (sys_stats.mem_percent >= 90) {
        mem_color = colors->workspace_urgent;
    } else if (sys_stats.mem_percent >= 75) {
        mem_color = 0xFFA500;
    }
    mem_color = ambient_glow_bg(mem_color, dwn->ambient_phase + PHASE_OFFSET_STATS);

    if (sys_stats.mem_total_mb >= 1024) {
        len = snprintf(stats_buf, sizeof(stats_buf), "MEM:%.1fG/%dG",
                       sys_stats.mem_used_mb / 1024.0f, sys_stats.mem_total_mb / 1024);
    } else {
        len = snprintf(stats_buf, sizeof(stats_buf), "MEM:%dM/%dM",
                       sys_stats.mem_used_mb, sys_stats.mem_total_mb);
    }
    panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, mem_color);
    current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING;

    unsigned long load_color = colors->panel_fg;
    if (sys_stats.load_1min >= 4.0f) {
        load_color = colors->workspace_urgent;
    } else if (sys_stats.load_1min >= 2.0f) {
        load_color = 0xFFA500;
    }
    load_color = ambient_glow_bg(load_color, dwn->ambient_phase + PHASE_OFFSET_STATS);

    len = snprintf(stats_buf, sizeof(stats_buf), "Load:%.2f %.2f %.2f",
                   sys_stats.load_1min, sys_stats.load_5min, sys_stats.load_15min);
    panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, load_color);
    current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING;

    MultiBatteryState multi_bat = systray_get_multi_battery_snapshot();
    if (multi_bat.count > 0) {
        unsigned long bat_color = colors->panel_fg;
        if (multi_bat.combined_percentage <= 20 && !multi_bat.any_charging) {
            bat_color = colors->workspace_urgent;
        } else if (multi_bat.combined_percentage <= 40 && !multi_bat.any_charging) {
            bat_color = 0xFFA500;
        } else if (multi_bat.any_charging) {
            bat_color = colors->workspace_active;
        }
        bat_color = ambient_glow_bg(bat_color, dwn->ambient_phase + PHASE_OFFSET_STATS);

        const char *bat_icon = multi_bat.any_charging ? "[+]" : "[=]";
        if (multi_bat.count == 1) {
            len = snprintf(stats_buf, sizeof(stats_buf), "%s%d%%", bat_icon, multi_bat.combined_percentage);
        } else {
            int offset = snprintf(stats_buf, sizeof(stats_buf), "%s%d%% (", bat_icon, multi_bat.combined_percentage);
            for (int i = 0; i < multi_bat.count && offset < (int)sizeof(stats_buf) - 10; i++) {
                if (i > 0) {
                    offset += snprintf(stats_buf + offset, sizeof(stats_buf) - offset, " ");
                }
                offset += snprintf(stats_buf + offset, sizeof(stats_buf) - offset, "B%d:%d%%",
                                   i + 1, multi_bat.batteries[i].percentage);
            }
            snprintf(stats_buf + offset, sizeof(stats_buf) - offset, ")");
            len = strlen(stats_buf);
        }
        panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, bat_color);
        current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING;
    }

    *width = current_x - x;
}

static void panel_render_key_counter(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        *width = 0;
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        *width = 0;
        return;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    char buf[32];
    unsigned long count = dwn->key_press_count;
    int len;

    if (count >= 1000000) {
        len = snprintf(buf, sizeof(buf), "Keys:%.1fM", count / 1000000.0);
    } else if (count >= 1000) {
        len = snprintf(buf, sizeof(buf), "Keys:%.1fK", count / 1000.0);
    } else {
        len = snprintf(buf, sizeof(buf), "Keys:%lu", count);
    }

    long now = get_time_ms();
    bool flickering = (now - dwn->key_delete_flicker_time) < 100;

    unsigned long fg;
    if (flickering) {
        unsigned long base = colors->panel_fg;
        int r = (int)((base >> 16) & 0xFF);
        int g = (int)((base >> 8) & 0xFF);
        int b = (int)(base & 0xFF);
        r = r + (255 - r) / 2;
        g = g + (255 - g) / 2;
        b = b + (255 - b) / 2;
        fg = ((unsigned long)r << 16) | ((unsigned long)g << 8) | (unsigned long)b;
    } else {
        fg = ambient_glow_bg(colors->panel_fg,
                             dwn->ambient_phase + PHASE_OFFSET_KEY_COUNTER);
    }

    if (flickering) {
        panel_draw_text_bold(panel->buffer, x, text_y, buf, len, fg);
    } else {
        panel_draw_text(panel->buffer, x, text_y, buf, len, fg);
    }
    *width = panel_text_width(buf, len);
}

static void panel_render_mouse_distance(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        *width = 0;
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        *width = 0;
        return;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    char buf[32];
    double cm = dwn->mouse_distance_pixels / 37.8;
    int len;

    if (cm >= 100000.0) {
        len = snprintf(buf, sizeof(buf), "Mouse:%.2fkm", cm / 100000.0);
    } else if (cm >= 100.0) {
        len = snprintf(buf, sizeof(buf), "Mouse:%.1fm", cm / 100.0);
    } else {
        len = snprintf(buf, sizeof(buf), "Mouse:%.0fcm", cm);
    }

    unsigned long fg = ambient_glow_bg(colors->panel_fg,
                                        dwn->ambient_phase + PHASE_OFFSET_MOUSE_DIST);
    panel_draw_text(panel->buffer, x, text_y, buf, len, fg);
    *width = panel_text_width(buf, len);
}

static void update_disk_space(void)
{
    long now = get_time_ms();
    if (now - disk_last_update < DISK_UPDATE_INTERVAL_MS && disk_last_update > 0) {
        return;
    }

    struct statvfs stat;
    if (statvfs("/", &stat) == 0) {
        unsigned long long block_size = stat.f_frsize;
        disk_free_gb = (double)(stat.f_bavail * block_size) / (1024.0 * 1024.0 * 1024.0);
        disk_total_gb = (double)(stat.f_blocks * block_size) / (1024.0 * 1024.0 * 1024.0);
    }
    disk_last_update = now;
}

static void panel_render_disk_space(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        *width = 0;
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        *width = 0;
        return;
    }

    update_disk_space();

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    char buf[32];
    int len;

    if (disk_total_gb >= 1000.0) {
        len = snprintf(buf, sizeof(buf), "Disk:%.1fT/%.1fT",
                       disk_free_gb / 1024.0, disk_total_gb / 1024.0);
    } else {
        len = snprintf(buf, sizeof(buf), "Disk:%.0fG/%.0fG",
                       disk_free_gb, disk_total_gb);
    }

    int used_percent = 0;
    if (disk_total_gb > 0.0) {
        used_percent = (int)(((disk_total_gb - disk_free_gb) / disk_total_gb) * 100.0);
    }

    unsigned long color = colors->panel_fg;
    if (used_percent >= 90) {
        color = colors->workspace_urgent;
    } else if (used_percent >= 75) {
        color = 0xFFA500;
    }

    unsigned long fg = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_DISK_SPACE);
    panel_draw_text(panel->buffer, x, text_y, buf, len, fg);
    *width = panel_text_width(buf, len);
}

static void panel_render_top_process(Panel *panel, int x, int *width)
{
    if (panel == NULL || width == NULL) {
        *width = 0;
        return;
    }
    if (dwn->xft_font == NULL && dwn->font == NULL) {
        *width = 0;
        return;
    }

    int mem_count = sys_stats.top_mem_count;
    int cpu_count = sys_stats.top_cpu_count;
    if (mem_count == 0 && cpu_count == 0) {
        *width = 0;
        return;
    }

    long now = get_time_ms();
    if (now - sys_stats.process_toggle_time >= 3000) {
        sys_stats.process_show_cpu = !sys_stats.process_show_cpu;
        sys_stats.process_toggle_time = now;
    }

    const ColorScheme *colors = config_get_colors();
    int text_y = panel_text_y(panel->height);

    char buf[128];
    char name_buf[TOP_PROCESS_NAME_MAX_LEN + 1];
    int len;
    unsigned long color;
    const char *proc_name;

    int prefix_width = panel_text_width("M:", 2);
    int suffix_width = panel_text_width(" 100%", 5);
    int available_for_name = TOP_PROCESS_WIDGET_WIDTH - prefix_width - suffix_width;

    bool show_cpu = sys_stats.process_show_cpu;
    if (show_cpu && cpu_count == 0) show_cpu = false;
    if (!show_cpu && mem_count == 0) show_cpu = true;

    if (show_cpu && cpu_count > 0) {
        int percent = sys_stats.top_cpu_percents[0];
        color = colors->panel_fg;
        if (percent >= 50) {
            color = 0x4169E1;
        } else if (percent >= 25) {
            color = 0x00CED1;
        }
        color = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_TOP_CPU);

        proc_name = sys_stats.top_cpu_processes[0];
        size_t name_len = strlen(proc_name);
        if (name_len > TOP_PROCESS_NAME_MAX_LEN) name_len = TOP_PROCESS_NAME_MAX_LEN;
        memcpy(name_buf, proc_name, name_len);
        name_buf[name_len] = '\0';
        while (strlen(name_buf) > 2 && panel_text_width(name_buf, (int)strlen(name_buf)) > available_for_name) {
            name_buf[strlen(name_buf) - 1] = '\0';
        }

        len = snprintf(buf, sizeof(buf), "C:%s %d%%", name_buf, percent);
    } else if (mem_count > 0) {
        int percent = sys_stats.top_mem_percents[0];
        color = colors->panel_fg;
        if (percent >= 50) {
            color = colors->workspace_urgent;
        } else if (percent >= 25) {
            color = 0xFFA500;
        }
        color = ambient_glow_bg(color, dwn->ambient_phase + PHASE_OFFSET_TOP_MEM);

        proc_name = sys_stats.top_mem_processes[0];
        size_t name_len = strlen(proc_name);
        if (name_len > TOP_PROCESS_NAME_MAX_LEN) name_len = TOP_PROCESS_NAME_MAX_LEN;
        memcpy(name_buf, proc_name, name_len);
        name_buf[name_len] = '\0';
        while (strlen(name_buf) > 2 && panel_text_width(name_buf, (int)strlen(name_buf)) > available_for_name) {
            name_buf[strlen(name_buf) - 1] = '\0';
        }

        len = snprintf(buf, sizeof(buf), "M:%s %d%%", name_buf, percent);
    } else {
        *width = 0;
        return;
    }

    panel_draw_text(panel->buffer, x, text_y, buf, len, color);
    *width = TOP_PROCESS_WIDGET_WIDTH;
}


void panel_invalidate(void)
{
    panels_dirty = true;
    if (dwn->top_panel) dwn->top_panel->dirty = true;
    if (dwn->bottom_panel) dwn->bottom_panel->dirty = true;
}

void panel_invalidate_taskbar(void)
{
    panels_dirty = true;
    if (dwn->top_panel) dwn->top_panel->dirty = true;
}

void panel_invalidate_news(void)
{
    news_region_dirty = true;
}

void panel_set_news_region(int x, int width)
{
    news_region_x = x;
    news_region_width = width;
}

void panel_render_news_only(void)
{
    if (dwn->bottom_panel == NULL || !news_region_dirty) {
        return;
    }
    if (news_region_width <= 0) {
        news_region_dirty = false;
        return;
    }

    Panel *panel = dwn->bottom_panel;
    Display *dpy = dwn->display;
    const ColorScheme *colors = config_get_colors();

    unsigned long bg = ambient_glow_bg(colors->panel_bg,
                                        dwn->ambient_phase + PHASE_OFFSET_PANEL_BG);
    XSetForeground(dpy, dwn->gc, bg);
    XFillRectangle(dpy, panel->buffer, dwn->gc,
                   news_region_x, 0, (unsigned int)news_region_width, (unsigned int)panel->height);

    int width;
    news_render(panel, news_region_x, news_region_width, &width);

    XCopyArea(dpy, panel->buffer, panel->window, dwn->gc,
              news_region_x, 0, (unsigned int)news_region_width, (unsigned int)panel->height,
              news_region_x, 0);

    news_region_dirty = false;
}

bool panel_needs_render(void)
{
    return panels_dirty;
}

bool panel_news_needs_render(void)
{
    return news_region_dirty;
}

void panel_init_systray(void)
{
    LOG_DEBUG("System tray initialization (placeholder)");
}

void panel_add_systray_icon(Window icon)
{
    (void)icon;
    LOG_DEBUG("Add systray icon (placeholder)");
}

void panel_remove_systray_icon(Window icon)
{
    (void)icon;
    LOG_DEBUG("Remove systray icon (placeholder)");
}
