/*---------------------------------------
 SDL based Task Manager for Windows.
 By Grok Stunt Jockey Tony @tswain555 on X
 compile mingw64->urct64.
  "Added back" Set Process Priority.
  Double click on a task to see details.
Compile: MinGW64 -> UCRT64 
gcc -g -o sdl_tasks sdl3_tasks.c -lSDL3_ttf -lSDL3 -mwindows
*/
#include <SDL3/SDL.h>
#include <SDL3/SDL_ttf.h>
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>  // For fmax/fmin

#define MAX_PROCESSES 1000
#define WINDOW_WIDTH 840
#define WINDOW_HEIGHT 600
#define REFRESH_INTERVAL 2000  // 2 seconds
#define FONT_SIZE 14
#define DOUBLE_CLICK_TIME 500  // ms
#define POPUP_WIDTH 600
#define POPUP_HEIGHT 400
#define MAX_INFO_LINES 20
#define MAX_LINE_LEN 512  // For truncation
#define SCROLLBAR_WIDTH 20
#define SCROLL_SENSITIVITY 3  // Rows per wheel notch
#define THUMB_MIN_HEIGHT 20
#define LAUNCH_BUF_SIZE 512

typedef struct
{
    DWORD pid;
    char name[256];
    DWORD priority;
} ProcessInfo;

typedef struct
{
    char *text;
    int y_offset;
} InfoLine;

int getProcesses(ProcessInfo *processes, int *count)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
    {
        return 0;
    }

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);

    *count = 0;

    if (Process32First(hSnapshot, &pe32))
    {
        do
        {
            if (*count < MAX_PROCESSES)
            {
                processes[*count].pid = pe32.th32ProcessID;
                strncpy(processes[*count].name, pe32.szExeFile, sizeof(processes[*count].name) - 1);
                processes[*count].name[sizeof(processes[*count].name) - 1] = '\0';
                (*count)++;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }

    CloseHandle(hSnapshot);

    // Fetch priorities
    for (int j = 0; j < *count; j++)
    {
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[j].pid);
        if (hProcess)
        {
            processes[j].priority = GetPriorityClass(hProcess);
            CloseHandle(hProcess);
        }
        else
        {
            processes[j].priority = 0;
        }
    }

    return 1;
}

void updateWindowTitle(SDL_Window *window, int numProcesses)
{
    char title[256];
    snprintf(title, sizeof(title), "SuperTask V1.0 - %d", numProcesses);
    SDL_SetWindowTitle(window, title);
}

int killProcess(DWORD pid)
{
    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
    if (hProcess == NULL)
    {
        return 0;
    }

    int result = TerminateProcess(hProcess, 0);
    CloseHandle(hProcess);
    return result;
}

int setProcessPriority(DWORD pid, DWORD priority)
{
    HANDLE hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, pid);
    if (hProcess == NULL)
    {
        return 0;
    }

    int result = SetPriorityClass(hProcess, priority);
    CloseHandle(hProcess);
    return result;
}

int getProcessInfo(DWORD pid, InfoLine *lines, int *num_lines)
{
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (hProcess == NULL)
    {
        char buf[MAX_LINE_LEN];
        DWORD err = GetLastError();
        snprintf(buf, sizeof(buf), "Failed to open process (Error %lu: %s). Run as admin?", (unsigned long)err, err == 5 ? "Access denied" : "Unknown error");
        lines[0].text = strdup(buf);
        lines[0].y_offset = 0;
        *num_lines = 1;
        return 0;
    }

    char buf[MAX_LINE_LEN];
    char temp_buf[MAX_LINE_LEN];
    int idx = 0;

    // Basic
    snprintf(buf, sizeof(buf), "PID: %lu", (unsigned long)pid);
    lines[idx].text = strdup(buf);
    lines[idx].y_offset = 0;
    idx++;

    // Executable name
    char exename[MAX_PATH] = {0};  // Zero-init for safety
    DWORD name_len = GetModuleBaseNameA(hProcess, NULL, exename, sizeof(exename));
    if (name_len > 0 && name_len < sizeof(exename))
    {
        snprintf(temp_buf, sizeof(temp_buf), "Executable: %s", exename);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
    }
    else
    {
        snprintf(buf, sizeof(buf), "Executable: Unknown");
    }
    lines[idx].text = strdup(buf);
    lines[idx].y_offset = 0;
    idx++;

    // Memory
    PROCESS_MEMORY_COUNTERS pmc = {0};
    if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
    {
        snprintf(temp_buf, sizeof(temp_buf), "Working Set Size: %lu KB", pmc.WorkingSetSize / 1024);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;

        snprintf(temp_buf, sizeof(temp_buf), "Pagefile Usage: %lu KB", pmc.PagefileUsage / 1024);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;

        snprintf(temp_buf, sizeof(temp_buf), "Peak Working Set: %lu KB", pmc.PeakWorkingSetSize / 1024);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;
    }
    else
    {
        strcpy(buf, "Memory info: Unavailable");
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;
    }

    // Times
    FILETIME createTime = {0}, exitTime = {0}, kernelTime = {0}, userTime = {0};
    if (GetProcessTimes(hProcess, &createTime, &exitTime, &kernelTime, &userTime))
    {
        // Creation time
        SYSTEMTIME st = {0};
        bool valid_date = false;
        if (FileTimeToSystemTime(&createTime, &st))
        {
            // Validate SYSTEMTIME
            if (st.wYear >= 1970 && st.wYear <= 2100 &&
                st.wMonth >= 1 && st.wMonth <= 12 &&
                st.wDay >= 1 && st.wDay <= 31 &&
                st.wHour >= 0 && st.wHour <= 23 &&
                st.wMinute >= 0 && st.wMinute <= 59 &&
                st.wSecond >= 0 && st.wSecond <= 59)
            {
                valid_date = true;
                snprintf(temp_buf, sizeof(temp_buf), "Creation Time: %04d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
                strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
                buf[MAX_LINE_LEN - 1] = '\0';
            }
        }
        if (!valid_date)
        {
            strcpy(buf, "Creation Time: Invalid");
        }
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;

        // Kernel/User times
        ULARGE_INTEGER kernelLi = { kernelTime.dwLowDateTime, kernelTime.dwHighDateTime };
        ULARGE_INTEGER userLi = { userTime.dwLowDateTime, userTime.dwHighDateTime };
        snprintf(temp_buf, sizeof(temp_buf), "Kernel Time: %.2f s", (double)kernelLi.QuadPart / 10000000.0);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;

        snprintf(temp_buf, sizeof(temp_buf), "User Time: %.2f s", (double)userLi.QuadPart / 10000000.0);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;
    }
    else
    {
        strcpy(buf, "Process times: Unavailable");
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;
    }

    // Handles
    DWORD handleCount = 0;
    if (GetProcessHandleCount(hProcess, &handleCount))
    {
        snprintf(temp_buf, sizeof(temp_buf), "Handle Count: %lu", (unsigned long)handleCount);
        strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
        buf[MAX_LINE_LEN - 1] = '\0';
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;
    }
    else
    {
        strcpy(buf, "Handle count: Unavailable");
        lines[idx].text = strdup(buf);
        lines[idx].y_offset = 0;
        idx++;
    }

    // Priority
    DWORD priority = GetPriorityClass(hProcess);
    const char *pri_str = "Unknown";
    switch (priority)
    {
        case REALTIME_PRIORITY_CLASS: pri_str = "Realtime"; break;
        case HIGH_PRIORITY_CLASS: pri_str = "High"; break;
        case ABOVE_NORMAL_PRIORITY_CLASS: pri_str = "Above Normal"; break;
        case NORMAL_PRIORITY_CLASS: pri_str = "Normal"; break;
        case BELOW_NORMAL_PRIORITY_CLASS: pri_str = "Below Normal"; break;
        case IDLE_PRIORITY_CLASS: pri_str = "Idle"; break;
    }
    snprintf(temp_buf, sizeof(temp_buf), "Priority Class: %s", pri_str);
    strncpy(buf, temp_buf, MAX_LINE_LEN - 1);
    buf[MAX_LINE_LEN - 1] = '\0';
    lines[idx].text = strdup(buf);
    lines[idx].y_offset = 0;
    idx++;

    CloseHandle(hProcess);
    *num_lines = idx;
    return 1;
}

int main(int argc, char *argv[])
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return 1;
    }

    if (TTF_Init() == -1)
    {
        SDL_Log("TTF_Init failed: %s", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    TTF_Font *font = TTF_OpenFont("C:/windows/fonts/cour.ttf", FONT_SIZE);
    if (!font)
    {
        SDL_Log("TTF_OpenFont failed: %s", SDL_GetError());
        TTF_Quit();
        SDL_Quit();
        return 1;
    }
    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);

    int line_height = TTF_GetFontLineSkip(font);
    int menu_height = line_height + 10;

    SDL_Window *window;
    SDL_Renderer *renderer;
    if (!SDL_CreateWindowAndRenderer("SuperTask V1.0 - 0", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &window, &renderer))
    {
        SDL_Log("SDL_CreateWindowAndRenderer failed: %s", SDL_GetError());
        TTF_CloseFont(font);
        TTF_Quit();
        SDL_Quit();
        return 1;
    }

    DWORD self_pid = GetCurrentProcessId();

    ProcessInfo processes[MAX_PROCESSES];
    int numProcesses = 0;
    getProcesses(processes, &numProcesses);
    updateWindowTitle(window, numProcesses);

    int selected = -1;
    Uint32 lastRefresh = SDL_GetTicks();
    Uint32 lastClickTime = 0;
    int lastClickX = 0, lastClickY = 0;

    // Scrollbar state
    int scroll_offset = 0;
    bool scrollbar_visible = false;
    bool dragging_thumb = false;
    int thumb_click_offset = 0;  // Offset from top of thumb to click position

    // Launch state
    bool show_launch = false;
    char launch_buf[LAUNCH_BUF_SIZE] = {0};
    int launch_pos = 0;
    Uint32 launch_start_time = 0;

    // Get initial window size
    int win_width, win_height;
    SDL_GetWindowSize(window, &win_width, &win_height);

    bool running = true;
    bool show_popup = false;
    DWORD popup_pid = 0;
    InfoLine info_lines[MAX_INFO_LINES];
    int num_info_lines = 0;

    SDL_Event event;
    SDL_Color white = {255, 255, 255, 255};
    SDL_Color yellow = {255, 255, 0, 255};
    SDL_Color gray = {128, 128, 128, 255};  // For popup title and scrollbar
    SDL_Color scrollbar_thumb = {100, 149, 237, 255};  // Cornflower blue
    SDL_Color normal_color = {0, 255, 0, 255};  // Green
    SDL_Color below_color = {0, 0, 255, 255};  // Blue
    SDL_Color high_color = {255, 255, 0, 255};  // Yellow
    SDL_Color realtime_color = {255, 0, 0, 255};  // Red

    while (running)
    {
        while (SDL_PollEvent(&event))
        {
            if (show_launch)
            {
                switch (event.type)
                {
                    case SDL_EVENT_QUIT:
                        running = false;
                        show_launch = false;
                        break;
                    case SDL_EVENT_KEY_DOWN:
                    {
                        SDL_Keycode key = event.key.key;
                        if (key == SDLK_RETURN)
                        {
                            // Launch process
                            STARTUPINFOA si = {0};
                            si.cb = sizeof(si);
                            PROCESS_INFORMATION pi = {0};
                            if (CreateProcessA(NULL, launch_buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
                            {
                                CloseHandle(pi.hProcess);
                                CloseHandle(pi.hThread);
                                getProcesses(processes, &numProcesses);
                                updateWindowTitle(window, numProcesses);
                                if (selected >= numProcesses) selected = -1;
                            }
                            memset(launch_buf, 0, sizeof(launch_buf));
                            launch_pos = 0;
                            show_launch = false;
                            SDL_StopTextInput(window);
                        }
                        else if (key == SDLK_BACKSPACE && launch_pos > 0)
                        {
                            launch_pos--;
                            launch_buf[launch_pos] = '\0';
                        }
                        else if (key == SDLK_ESCAPE)
                        {
                            memset(launch_buf, 0, sizeof(launch_buf));
                            launch_pos = 0;
                            show_launch = false;
                            SDL_StopTextInput(window);
                        }
                    }
                    break;
                    case SDL_EVENT_TEXT_INPUT:
                        if (launch_pos < LAUNCH_BUF_SIZE - 1)
                        {
                            strncat(launch_buf, event.text.text, LAUNCH_BUF_SIZE - launch_pos - 1);
                            launch_pos += strlen(event.text.text);
                        }
                        break;
                    case SDL_EVENT_MOUSE_BUTTON_DOWN:
                        memset(launch_buf, 0, sizeof(launch_buf));
                        launch_pos = 0;
                        show_launch = false;
                        SDL_StopTextInput(window);
                        break;
                }
                continue;
            }

            if (show_popup)
            {
                // Modal popup: handle priority changes or close
                switch (event.type)
                {
                    case SDL_EVENT_QUIT:
                        running = false;
                        show_popup = false;
                        break;
                    case SDL_EVENT_KEY_DOWN:
                    {
                        SDL_Keycode key = event.key.key;
                        DWORD new_priority = 0;
                        bool valid_key = true;
                        switch (key)
                        {
                            case SDLK_R:
                                new_priority = REALTIME_PRIORITY_CLASS;
                                break;
                            case SDLK_H:
                                new_priority = HIGH_PRIORITY_CLASS;
                                break;
                            case SDLK_A:
                                new_priority = ABOVE_NORMAL_PRIORITY_CLASS;
                                break;
                            case SDLK_N:
                                new_priority = NORMAL_PRIORITY_CLASS;
                                break;
                            case SDLK_B:
                                new_priority = BELOW_NORMAL_PRIORITY_CLASS;
                                break;
                            case SDLK_I:
                                new_priority = IDLE_PRIORITY_CLASS;
                                break;
                            default:
                                valid_key = false;
                                break;
                        }
                        if (valid_key && new_priority != 0)
                        {
                            if (setProcessPriority(popup_pid, new_priority))
                            {
                                // Refresh info
                                getProcessInfo(popup_pid, info_lines, &num_info_lines);
                            }
                        }
                        else
                        {
                            // Any other key closes
                            show_popup = false;
                        }
                    }
                    break;
                    case SDL_EVENT_MOUSE_BUTTON_DOWN:
                        show_popup = false;
                        break;
                }
                continue;
            }

            switch (event.type)
            {
                case SDL_EVENT_QUIT:
                    running = false;
                    break;
                case SDL_EVENT_WINDOW_RESIZED:
                    SDL_GetWindowSize(window, &win_width, &win_height);
                    // Recalculate visible rows and clamp scroll
                    int header_end = 10 + line_height;
                    int list_height = win_height - header_end - menu_height - 10;
                    int visible_rows = fmax(1, list_height / line_height);
                    int max_offset = fmax(0, numProcesses - visible_rows);
                    scroll_offset = fmin(scroll_offset, max_offset);
                    break;
                case SDL_EVENT_KEY_DOWN:
                    switch (event.key.key)
                    {
                        case 'r':
                        case 'R':
                            getProcesses(processes, &numProcesses);
                            updateWindowTitle(window, numProcesses);
                            if (selected >= numProcesses) selected = -1;
                            scroll_offset = 0;  // Reset on manual refresh
                            dragging_thumb = false;
                            break;
                        case 'k':
                        case 'K':
                            if (selected >= 0 && selected < numProcesses)
                            {
                                if (killProcess(processes[selected].pid))
                                {
                                }
                                else
                                {
                                }
                                getProcesses(processes, &numProcesses);
                                updateWindowTitle(window, numProcesses);
                                selected = -1;
                                scroll_offset = 0;  // Reset after kill
                                dragging_thumb = false;
                            }
                            break;
                        case 'l':
                        case 'L':
                            show_launch = true;
                            SDL_StartTextInput(window);
                            memset(launch_buf, 0, sizeof(launch_buf));
                            launch_pos = 0;
                            launch_start_time = SDL_GetTicks();
                            break;
                        case SDLK_RETURN:
                        case SDLK_KP_ENTER:  // Handle numpad Enter too
                            if (selected >= 0 && selected < numProcesses)
                            {
                                popup_pid = processes[selected].pid;
                                getProcessInfo(popup_pid, info_lines, &num_info_lines);  // Always load, even if partial
                                show_popup = true;
                            }
                            break;
                        case SDLK_ESCAPE:
                            running = false;
                            break;
                        case SDLK_UP:
                            if (selected > 0) selected--;
                            break;
                        case SDLK_DOWN:
                            if (selected < numProcesses - 1) selected++;
                            break;
                    }
                    break;
                case SDL_EVENT_MOUSE_WHEEL:
                    if (!show_popup && !show_launch)
                    {
                        // Recalc for wheel
                        int header_end = 10 + line_height;
                        int list_height = win_height - header_end - menu_height - 10;
                        int visible_rows = fmax(1, list_height / line_height);
                        int max_offset = fmax(0, numProcesses - visible_rows);
                        int delta = event.wheel.y * SCROLL_SENSITIVITY;
                        scroll_offset = fmax(0, scroll_offset - delta);
                        scroll_offset = fmin(scroll_offset, max_offset);
                        dragging_thumb = false;  // Stop drag on wheel
                    }
                    break;
                case SDL_EVENT_MOUSE_BUTTON_DOWN:
                    if (event.button.button == SDL_BUTTON_LEFT && !show_popup && !show_launch)
                    {
                        int mouseX = event.button.x;
                        int mouseY = event.button.y;
                        Uint32 now = SDL_GetTicks();

                        // Check for scrollbar thumb drag
                        if (scrollbar_visible)
                        {
                            int header_end = 10 + line_height;
                            int list_height = win_height - header_end - menu_height - 10;
                            int track_start_y = header_end;
                            int track_height = list_height;
                            int visible_rows = fmax(1, list_height / line_height);
                            int max_scroll = fmax(0, numProcesses - visible_rows);
                            float thumb_ratio_local = fmin(1.0f, (float)visible_rows / fmax(1, numProcesses));
                            float thumb_height_f = fmax(THUMB_MIN_HEIGHT, thumb_ratio_local * track_height);
                            int thumb_height = (int)thumb_height_f;
                            float thumb_pos_f = (max_scroll > 0) ? (scroll_offset / (float)max_scroll) * (track_height - thumb_height) : 0.0f;
                            int thumb_y = track_start_y + (int)thumb_pos_f;

                            if (mouseX >= win_width - SCROLLBAR_WIDTH && mouseX <= win_width &&
                                mouseY >= thumb_y && mouseY <= thumb_y + thumb_height)
                            {
                                dragging_thumb = true;
                                thumb_click_offset = mouseY - thumb_y;  // Distance from top of thumb to click
                                break;  // Consume event
                            }
                        }

                        // List selection
                        int headerOffset = 10 + line_height;
                        int rows_from_top = (mouseY - headerOffset) / line_height;
                        int list_width = win_width - (scrollbar_visible ? SCROLLBAR_WIDTH : 0);
                        bool valid_selection = (rows_from_top >= 0 && mouseX < list_width && mouseY >= headerOffset);

                        int idx = -1;
                        if (valid_selection)
                        {
                            // Recalc visible for accuracy
                            int list_height_sel = win_height - headerOffset - menu_height - 10;
                            int visible_rows_sel = fmax(1, list_height_sel / line_height);
                            if (rows_from_top < visible_rows_sel)
                            {
                                idx = scroll_offset + rows_from_top;
                                if (idx < numProcesses)
                                {
                                    valid_selection = true;
                                }
                                else
                                {
                                    valid_selection = false;
                                }
                            }
                            else
                            {
                                valid_selection = false;
                            }
                        }

                        if (valid_selection && abs(mouseX - lastClickX) < 10 && abs(mouseY - lastClickY) < 10 && (now - lastClickTime) < DOUBLE_CLICK_TIME)
                        {
                            // Double click detected
                            popup_pid = processes[idx].pid;
                            getProcessInfo(popup_pid, info_lines, &num_info_lines);
                            show_popup = true;
                        }
                        else if (valid_selection)
                        {
                            // Single click: select
                            selected = idx;
                        }

                        lastClickTime = now;
                        lastClickX = mouseX;
                        lastClickY = mouseY;
                    }
                    break;
                case SDL_EVENT_MOUSE_BUTTON_UP:
                    if (event.button.button == SDL_BUTTON_LEFT)
                    {
                        dragging_thumb = false;
                    }
                    break;
                case SDL_EVENT_MOUSE_MOTION:
                    if (dragging_thumb && scrollbar_visible)
                    {
                        int mouseY = event.motion.y;

                        int header_end = 10 + line_height;
                        int list_height = win_height - header_end - menu_height - 10;
                        int track_start_y = header_end;
                        int track_height = list_height;
                        int visible_rows = fmax(1, list_height / line_height);
                        int max_scroll = fmax(0, numProcesses - visible_rows);
                        float thumb_ratio_local = fmin(1.0f, (float)visible_rows / fmax(1, numProcesses));
                        float thumb_height_f = fmax(THUMB_MIN_HEIGHT, thumb_ratio_local * track_height);
                        int thumb_height = (int)thumb_height_f;

                        // Calculate new thumb top position based on mouse and click offset
                        int new_thumb_y = mouseY - thumb_click_offset;
                        new_thumb_y = fmax(track_start_y, fmin(new_thumb_y, track_start_y + track_height - thumb_height));

                        // Normalize to scroll offset
                        float normalized = (max_scroll > 0 && track_height > thumb_height) ?
                                           ((float)(new_thumb_y - track_start_y) / (track_height - thumb_height)) : 0.0f;
                        scroll_offset = (int)(normalized * max_scroll);
                        scroll_offset = fmax(0, fmin(scroll_offset, max_scroll));
                    }
                    break;
            }
        }

        // Update window size
        SDL_GetWindowSize(window, &win_width, &win_height);

        if (!show_popup && !show_launch)
        {
            // Auto-refresh - preserve scroll position proportionally
            Uint32 now = SDL_GetTicks();
            if (now - lastRefresh >= REFRESH_INTERVAL)
            {
                // Calculate current fraction before refresh
                int old_header_end = 10 + line_height;
                int old_list_height = win_height - old_header_end - menu_height - 10;
                int old_visible_rows = fmax(1, old_list_height / line_height);
                int old_max_offset = fmax(0, numProcesses - old_visible_rows);
                float scroll_fraction = (old_max_offset > 0) ? (float)scroll_offset / old_max_offset : 0.0f;

                getProcesses(processes, &numProcesses);
                updateWindowTitle(window, numProcesses);
                if (selected >= numProcesses) selected = -1;

                // Apply fraction to new max
                int new_header_end = 10 + line_height;
                int new_list_height = win_height - new_header_end - menu_height - 10;
                int new_visible_rows = fmax(1, new_list_height / line_height);
                int new_max_offset = fmax(0, numProcesses - new_visible_rows);
                scroll_offset = (int)(scroll_fraction * new_max_offset);
                scroll_offset = fmax(0, fmin(scroll_offset, new_max_offset));

                lastRefresh = now;
            }
        }

        // Recalculate scrollbar state
        int header_end = 10 + line_height;
        int list_height = win_height - header_end - menu_height - 10;
        int visible_rows = fmax(1, list_height / line_height);
        scrollbar_visible = (numProcesses > visible_rows);
        int max_offset = fmax(0, numProcesses - visible_rows);
        scroll_offset = fmin(scroll_offset, max_offset);
        float thumb_ratio = 0.0f;
        if (scrollbar_visible && numProcesses > 0)
        {
            thumb_ratio = fmin(1.0f, (float)visible_rows / numProcesses);
        }

        // Render
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        if (show_launch)
        {
            // Launch popup
            int launch_x = (win_width - POPUP_WIDTH) / 2;
            int launch_y = (win_height - POPUP_HEIGHT) / 2;

            // Semi-transparent background
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 128);
            SDL_FRect bg_rect = { 0, 0, (float)win_width, (float)win_height };
            SDL_RenderFillRect(renderer, &bg_rect);

            // Popup rect
            SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
            SDL_FRect popup_rect = { (float)launch_x, (float)launch_y, (float)POPUP_WIDTH, (float)POPUP_HEIGHT };
            SDL_RenderFillRect(renderer, &popup_rect);

            // Title
            int title_y = launch_y + 10;
            const char *launch_title = "Launch Process";
            SDL_Surface *surf = TTF_RenderText_Solid(font, launch_title, strlen(launch_title), gray);
            SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surf);
            SDL_FRect dst = { (float)(launch_x + 10), (float)title_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            // Input prompt
            int input_y = title_y + line_height + 10;
            char prompt[256];
            snprintf(prompt, sizeof(prompt), "Enter executable path: %s", launch_buf);
            if ((SDL_GetTicks() - launch_start_time) / 500 % 2)
            {
                // Blinking cursor
                strcat(prompt, "_");
            }
            surf = TTF_RenderText_Solid(font, prompt, strlen(prompt), white);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ (float)(launch_x + 10), (float)input_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            // Instructions
            int inst_y = input_y + line_height + 5;
            const char *inst_text = "Press Enter to launch, Backspace to delete, ESC to cancel";
            surf = TTF_RenderText_Solid(font, inst_text, strlen(inst_text), yellow);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ (float)(launch_x + 10), (float)inst_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);
        }
        else if (!show_popup)
        {
            int list_start_y = header_end;
            int list_end_y = win_height - menu_height - 10;
            int list_width = win_width - (scrollbar_visible ? SCROLLBAR_WIDTH : 0);

            // Headers
            int y = 10;
            int pidX = 10;
            int nameX = 100;

            SDL_Surface *surf = TTF_RenderText_Solid(font, "PID", strlen("PID"), white);
            SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surf);
            SDL_FRect dst = { (float)pidX, (float)y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            surf = TTF_RenderText_Solid(font, "Name", strlen("Name"), white);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ (float)nameX, (float)y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            // Process list (scrolled)
            y = list_start_y;
            int end_idx = fmin(numProcesses, scroll_offset + visible_rows);
            for (int i = scroll_offset; i < end_idx; i++)
            {
                int display_idx = i - scroll_offset;  // 0-based in visible area
                SDL_Color textColor = white;

                // Priority color
                switch (processes[i].priority)
                {
                    case NORMAL_PRIORITY_CLASS:
                        textColor = normal_color;
                        break;
                    case BELOW_NORMAL_PRIORITY_CLASS:
                        textColor = below_color;
                        break;
                    case HIGH_PRIORITY_CLASS:
                        textColor = high_color;
                        break;
                    case REALTIME_PRIORITY_CLASS:
                        textColor = realtime_color;
                        break;
                    default:
                        textColor = white;
                        break;
                }

                if (i == selected)
                {
                    textColor = yellow;  // Selected overrides
                }

                // PID
                char pidStr[32];
                snprintf(pidStr, sizeof(pidStr), "%lu", (unsigned long)processes[i].pid);
                surf = TTF_RenderText_Solid(font, pidStr, strlen(pidStr), textColor);
                tex = SDL_CreateTextureFromSurface(renderer, surf);
                dst = (SDL_FRect){ (float)pidX, (float)y, (float)surf->w, (float)surf->h };
                SDL_RenderTexture(renderer, tex, NULL, &dst);
                SDL_DestroyTexture(tex);
                SDL_DestroySurface(surf);

                // Name
                surf = TTF_RenderText_Solid(font, processes[i].name, strlen(processes[i].name), textColor);
                tex = SDL_CreateTextureFromSurface(renderer, surf);
                dst = (SDL_FRect){ (float)nameX, (float)y, (float)fmin(surf->w, list_width - nameX), (float)surf->h };
                SDL_RenderTexture(renderer, tex, NULL, &dst);
                SDL_DestroyTexture(tex);
                SDL_DestroySurface(surf);

                y += line_height;
            }

            // Scrollbar rendering
            if (scrollbar_visible)
            {
                int track_start_y = list_start_y;
                int track_end_y = list_end_y;
                int track_height = track_end_y - track_start_y;
                float thumb_height_f = fmax(THUMB_MIN_HEIGHT, thumb_ratio * track_height);
                int thumb_height = (int)thumb_height_f;
                int max_scroll = fmax(0, numProcesses - visible_rows);
                float thumb_pos_f = (max_scroll > 0) ? (scroll_offset / (float)max_scroll) * (track_height - thumb_height) : 0.0f;
                int thumb_y = track_start_y + (int)thumb_pos_f;

                // Track (gray background)
                SDL_SetRenderDrawColor(renderer, gray.r / 2, gray.g / 2, gray.b / 2, 255);
                SDL_FRect track_rect = { (float)(win_width - SCROLLBAR_WIDTH), (float)track_start_y, (float)SCROLLBAR_WIDTH, (float)track_height };
                SDL_RenderFillRect(renderer, &track_rect);

                // Thumb (blue)
                SDL_SetRenderDrawColor(renderer, scrollbar_thumb.r, scrollbar_thumb.g, scrollbar_thumb.b, 255);
                SDL_FRect thumb_rect = { (float)(win_width - SCROLLBAR_WIDTH), (float)thumb_y, (float)SCROLLBAR_WIDTH, (float)thumb_height };
                SDL_RenderFillRect(renderer, &thumb_rect);
            }

            // Menu
            y = win_height - menu_height;
            const char *instructions = "R: Refresh | K: Kill | L: Launch | Enter/DBClick: Details | Up/Down: Nav | ESC: Quit | Wheel: Scroll";
            surf = TTF_RenderText_Solid(font, instructions, strlen(instructions), white);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ 10.0f, (float)y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);
        }
        else
        {
            // Popup - center based on current size
            int popup_x = (win_width - POPUP_WIDTH) / 2;
            int popup_y = (win_height - POPUP_HEIGHT) / 2;

            // Semi-transparent background
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 128);
            SDL_FRect bg_rect = { 0, 0, (float)win_width, (float)win_height };
            SDL_RenderFillRect(renderer, &bg_rect);

            // Popup rect
            SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
            SDL_FRect popup_rect = { (float)popup_x, (float)popup_y, (float)POPUP_WIDTH, (float)POPUP_HEIGHT };
            SDL_RenderFillRect(renderer, &popup_rect);

            // Title
            int title_y = popup_y + 10;
            char title[256];
            snprintf(title, sizeof(title), "Process Details - PID %lu", (unsigned long)popup_pid);
            SDL_Surface *surf = TTF_RenderText_Solid(font, title, strlen(title), gray);
            SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surf);
            SDL_FRect dst = { (float)(popup_x + 10), (float)title_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            // Info lines (incremental y to prevent overlap)
            int info_y = title_y + line_height + 5;
            for (int i = 0; i < num_info_lines; i++)
            {
                surf = TTF_RenderText_Solid(font, info_lines[i].text, strlen(info_lines[i].text), white);
                tex = SDL_CreateTextureFromSurface(renderer, surf);
                dst = (SDL_FRect){ (float)(popup_x + 10), (float)info_y, (float)surf->w, (float)surf->h };
                SDL_RenderTexture(renderer, tex, NULL, &dst);
                SDL_DestroyTexture(tex);
                SDL_DestroySurface(surf);
                info_y += line_height;  // Advance y for next line
            }

            // Priority change instructions (split into two lines to avoid overflow)
            int pri_y = info_y + 5;
            const char *pri_part1 = "Change Priority: R=Realtime, H=High, A=Above Normal,";
            const char *pri_part2 = "N=Normal, B=Below Normal, I=Idle";
            surf = TTF_RenderText_Solid(font, pri_part1, strlen(pri_part1), yellow);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ (float)(popup_x + 10), (float)pri_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            pri_y += line_height;
            surf = TTF_RenderText_Solid(font, pri_part2, strlen(pri_part2), yellow);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ (float)(popup_x + 10), (float)pri_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);

            // Close instruction
            int close_y = popup_y + POPUP_HEIGHT - line_height - 10;
            const char *close_text = "Click anywhere or press any other key to close";
            surf = TTF_RenderText_Solid(font, close_text, strlen(close_text), white);
            tex = SDL_CreateTextureFromSurface(renderer, surf);
            dst = (SDL_FRect){ (float)(popup_x + 10), (float)close_y, (float)surf->w, (float)surf->h };
            SDL_RenderTexture(renderer, tex, NULL, &dst);
            SDL_DestroyTexture(tex);
            SDL_DestroySurface(surf);
        }

        SDL_RenderPresent(renderer);
        SDL_Delay(16);
    }

    // Cleanup info lines
    for (int i = 0; i < num_info_lines; i++)
    {
        free(info_lines[i].text);
    }

    TTF_CloseFont(font);
    TTF_Quit();
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
