/*
 * main.cpp
 * MontaukOS Disk Tool — entry point, event loop, state
 * Copyright (c) 2026 Daniel Hammer
 */

#include "disks.h"
#include <gui/standalone.hpp>

// ============================================================================
// Global state definitions
// ============================================================================

int g_win_w = INIT_W;
int g_win_h = INIT_H;

DiskToolState g_state;

const Color part_colors[NUM_PART_COLORS] = {
    Color::from_rgb(0x42, 0x7A, 0xB5),
    Color::from_rgb(0x5C, 0xA0, 0x5C),
    Color::from_rgb(0xCC, 0x88, 0x33),
    Color::from_rgb(0x99, 0x55, 0xBB),
    Color::from_rgb(0xCC, 0x55, 0x55),
    Color::from_rgb(0x44, 0xAA, 0xAA),
    Color::from_rgb(0xBB, 0xBB, 0x44),
    Color::from_rgb(0x88, 0x66, 0x44),
};

// ============================================================================
// Helpers
// ============================================================================

void set_status(const char* msg) {
    int i = 0;
    for (; i < 79 && msg[i]; i++) g_state.status[i] = msg[i];
    g_state.status[i] = '\0';
    g_state.status_time = montauk::get_milliseconds();
}

int get_disk_parts(int* indices, int max) {
    int count = 0;
    for (int i = 0; i < g_state.part_count && count < max; i++) {
        if (g_state.parts[i].blockDev == g_state.selected_disk) {
            indices[count++] = i;
        }
    }
    return count;
}

void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorSize) {
    uint64_t bytes = sectors * sectorSize;
    uint64_t gb = bytes / (1024ULL * 1024 * 1024);
    if (gb >= 1024) {
        uint64_t tb = gb / 1024;
        uint64_t frac = ((gb % 1024) * 10) / 1024;
        snprintf(buf, bufsize, "%lu.%lu TB", (unsigned)tb, (unsigned)frac);
    } else if (gb > 0) {
        uint64_t frac = ((bytes % (1024ULL * 1024 * 1024)) * 10) / (1024ULL * 1024 * 1024);
        snprintf(buf, bufsize, "%lu.%lu GB", (unsigned)gb, (unsigned)frac);
    } else {
        uint64_t mb = bytes / (1024ULL * 1024);
        snprintf(buf, bufsize, "%lu MB", (unsigned)mb);
    }
}

// ============================================================================
// Toolbar hit testing
// ============================================================================

static bool handle_toolbar_click(int mx, int my) {
    if (my >= TOOLBAR_H || my < TB_BTN_Y) return false;

    auto& dt = g_state;
    if (dt.disk_count == 0) return false;
    bool has_sel = dt.selected_part >= 0;

    // Disk selector buttons
    for (int i = 0; i < dt.disk_count; i++) {
        if (toolbar_disk_button_rect(i).contains(mx, my)) {
            dt.selected_disk = i;
            dt.selected_part = -1;
            dt.scroll_y = 0;
            return true;
        }
    }

    // Right-side buttons (must match render layout)
    if (toolbar_refresh_button_rect().contains(mx, my)) {
        disktool_refresh();
        set_status("Refreshed");
        return true;
    }

    if (has_sel && toolbar_mount_button_rect().contains(mx, my)) {
        do_mount_partition();
        return true;
    }

    if (has_sel && toolbar_format_button_rect().contains(mx, my)) {
        open_format_dialog();
        return true;
    }

    if (toolbar_newpart_button_rect().contains(mx, my)) {
        do_create_partition();
        return true;
    }

    return false;
}

// ============================================================================
// Mouse handling for content area
// ============================================================================

static bool handle_content_click(int mx, int my) {
    auto& dt = g_state;
    if (dt.disk_count == 0 || dt.selected_disk < 0) return false;

    int y = partition_map_y();

    // Partition map bar click
    int map_x = MAP_PAD;
    int map_w = g_win_w - MAP_PAD * 2;
    if (my >= y && my < y + MAP_H) {
        Montauk::DiskInfo& disk = dt.disks[dt.selected_disk];
        int part_indices[MAX_PARTS];
        int nparts = get_disk_parts(part_indices, MAX_PARTS);
        uint64_t total = disk.sectorCount;
        if (total > 0) {
            for (int pi = 0; pi < nparts; pi++) {
                Montauk::PartInfo& p = dt.parts[part_indices[pi]];
                int px = map_x + (int)((p.startLba * (uint64_t)map_w) / total);
                int pw = (int)((p.sectorCount * (uint64_t)map_w) / total);
                if (pw < 2) pw = 2;
                if (mx >= px && mx < px + pw) {
                    dt.selected_part = pi;
                    return true;
                }
            }
        }
        dt.selected_part = -1;
        return true;
    }

    // Partition list click
    if (my >= partition_list_y() && my < g_win_h - STATUS_H) {
        int row = (my - partition_list_y() + dt.scroll_y) / ITEM_H;
        int part_indices[MAX_PARTS];
        int nparts = get_disk_parts(part_indices, MAX_PARTS);
        if (row >= 0 && row < nparts)
            dt.selected_part = row;
        else
            dt.selected_part = -1;
        return true;
    }

    return false;
}

// ============================================================================
// New partition dialog mouse handling
// ============================================================================

static bool handle_newpart_dialog_click(int mx, int my, bool clicked) {
    auto& dlg = g_state.np_dlg;
    if (!dlg.open) return false;

    Rect confirm = dialog_primary_button_rect(NP_DLG_W, NP_DLG_H);
    Rect cancel = dialog_secondary_button_rect(NP_DLG_W, NP_DLG_H);
    dlg.hover_confirm = confirm.contains(mx, my);
    dlg.hover_cancel = cancel.contains(mx, my);

    if (!clicked) return true;

    if (dlg.hover_confirm) {
        newpart_dialog_confirm();
        return true;
    }

    if (dlg.hover_cancel) {
        close_newpart_dialog();
        return true;
    }

    return true;
}

// ============================================================================
// Format dialog mouse handling
// ============================================================================

static bool handle_format_dialog_click(int mx, int my, bool clicked) {
    auto& dlg = g_state.fmt_dlg;
    if (!dlg.open) return false;

    // Button hit testing
    Rect format_btn = dialog_primary_button_rect(FMT_DLG_W, FMT_DLG_H);
    Rect cancel_btn = dialog_secondary_button_rect(FMT_DLG_W, FMT_DLG_H);
    dlg.hover_format = format_btn.contains(mx, my);
    dlg.hover_cancel = cancel_btn.contains(mx, my);

    if (!clicked) return true;

    // FS type selector
    for (int i = 0; i < NUM_FS_TYPES; i++) {
        if (format_option_rect(FMT_DLG_W, i).contains(mx, my)) {
            dlg.selected_fs = i;
            return true;
        }
    }

    if (dlg.hover_format) {
        format_dialog_do_format();
        return true;
    }

    if (dlg.hover_cancel) {
        close_format_dialog();
        return true;
    }

    return true;
}

static int hovered_disk_button(int mx, int my) {
    for (int i = 0; i < g_state.disk_count; i++) {
        if (toolbar_disk_button_rect(i).contains(mx, my))
            return i;
    }
    return -1;
}

static int hovered_partition_row(int mx, int my) {
    if (g_state.disk_count == 0 || g_state.selected_disk < 0) return -1;
    if (my < partition_list_y() || my >= g_win_h - STATUS_H) return -1;

    int part_indices[MAX_PARTS];
    int nparts = get_disk_parts(part_indices, MAX_PARTS);
    int row = (my - partition_list_y() + g_state.scroll_y) / ITEM_H;
    if (row < 0 || row >= nparts) return -1;
    return row;
}

static int max_scroll_y() {
    int part_indices[MAX_PARTS];
    int nparts = get_disk_parts(part_indices, MAX_PARTS);
    int visible_h = g_win_h - STATUS_H - partition_list_y();
    int total_h = nparts * ITEM_H;
    if (total_h <= visible_h) return 0;
    return total_h - visible_h;
}

static void clamp_scroll() {
    g_state.scroll_y = gui_clamp(g_state.scroll_y, 0, max_scroll_y());
}

static bool handle_mouse(const Montauk::WinEvent& ev) {
    int prev_x = g_state.mouse_x;
    int prev_y = g_state.mouse_y;
    int prev_disk_hover = hovered_disk_button(prev_x, prev_y);
    int prev_part_hover = hovered_partition_row(prev_x, prev_y);
    bool prev_refresh_hover = toolbar_refresh_button_rect().contains(prev_x, prev_y);
    bool prev_mount_hover = toolbar_mount_button_rect().contains(prev_x, prev_y);
    bool prev_format_hover = toolbar_format_button_rect().contains(prev_x, prev_y);
    bool prev_newpart_hover = toolbar_newpart_button_rect().contains(prev_x, prev_y);

    g_state.mouse_x = ev.mouse.x;
    g_state.mouse_y = ev.mouse.y;

    bool redraw = false;
    redraw |= (prev_disk_hover != hovered_disk_button(g_state.mouse_x, g_state.mouse_y));
    redraw |= (prev_part_hover != hovered_partition_row(g_state.mouse_x, g_state.mouse_y));
    redraw |= (prev_refresh_hover != toolbar_refresh_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
    redraw |= (prev_mount_hover != toolbar_mount_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
    redraw |= (prev_format_hover != toolbar_format_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
    redraw |= (prev_newpart_hover != toolbar_newpart_button_rect().contains(g_state.mouse_x, g_state.mouse_y));

    if (ev.mouse.scroll != 0) {
        g_state.scroll_y -= ev.mouse.scroll * 20;
        clamp_scroll();
        return true;
    }

    bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
    if (clicked) {
        if (handle_toolbar_click(ev.mouse.x, ev.mouse.y) || handle_content_click(ev.mouse.x, ev.mouse.y))
            return true;
    }

    return redraw;
}

// ============================================================================
// Key handling
// ============================================================================

static bool handle_key(const Montauk::KeyEvent& key) {
    if (!key.pressed) return false;

    auto& dt = g_state;

    // New partition dialog keys
    if (dt.np_dlg.open) {
        if (key.scancode == 0x01) { // Escape
            close_newpart_dialog();
        } else if (key.ascii == '\n' || key.ascii == '\r') {
            newpart_dialog_confirm();
        }
        return true;
    }

    // Format dialog keys
    if (dt.fmt_dlg.open) {
        if (key.scancode == 0x01) { // Escape
            close_format_dialog();
        } else if (key.scancode == 0x48 && dt.fmt_dlg.selected_fs > 0) {
            dt.fmt_dlg.selected_fs--;
        } else if (key.scancode == 0x50 && dt.fmt_dlg.selected_fs < NUM_FS_TYPES - 1) {
            dt.fmt_dlg.selected_fs++;
        } else if (key.ascii == '\n' || key.ascii == '\r') {
            format_dialog_do_format();
        }
        return true;
    }

    int part_indices[MAX_PARTS];
    int nparts = get_disk_parts(part_indices, MAX_PARTS);

    if (key.scancode == 0x01) { // Escape
        return false; // signal quit
    } else if (key.scancode == 0x48) { // Up
        if (dt.selected_part > 0) dt.selected_part--;
        else if (nparts > 0) dt.selected_part = 0;
    } else if (key.scancode == 0x50) { // Down
        if (dt.selected_part < nparts - 1) dt.selected_part++;
    } else if (key.scancode == 0x4B) { // Left
        if (dt.selected_disk > 0) { dt.selected_disk--; dt.selected_part = -1; dt.scroll_y = 0; }
    } else if (key.scancode == 0x4D) { // Right
        if (dt.selected_disk < dt.disk_count - 1) { dt.selected_disk++; dt.selected_part = -1; dt.scroll_y = 0; }
    } else if (key.ascii == 'n' || key.ascii == 'N') {
        do_create_partition();
    } else if (key.ascii == 'm' || key.ascii == 'M') {
        do_mount_partition();
    } else if (key.ascii == 'f' || key.ascii == 'F') {
        open_format_dialog();
    } else {
        return true; // consumed but no action
    }
    return true;
}

// ============================================================================
// Entry point
// ============================================================================

extern "C" void _start() {
    montauk::memset(&g_state, 0, sizeof(g_state));
    g_state.selected_disk = 0;
    g_state.selected_part = -1;
    g_state.mouse_x = -1;
    g_state.mouse_y = -1;

    if (!fonts::init())
        montauk::exit(1);

    disktool_refresh();

    WsWindow win;
    if (!win.create("Disks", INIT_W, INIT_H))
        montauk::exit(1);

    uint32_t* pixels = win.pixels;

    render(pixels);
    win.present();

    while (true) {
        Montauk::WinEvent ev;
        bool redraw_main = false;
        bool redraw_dlg = false;

        bool redraw_np = false;

        // Poll new partition dialog window
        if (g_state.np_dlg.open) {
            int dr = montauk::win_poll(g_state.np_dlg.win_id, &ev);
            if (dr > 0) {
                if (ev.type == 3) { // close
                    close_newpart_dialog();
                    redraw_main = true;
                } else if (ev.type == 0 && ev.key.pressed) {
                    handle_key(ev.key);
                    redraw_np = g_state.np_dlg.open;
                    redraw_main = true;
                } else if (ev.type == 1) {
                    bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
                    handle_newpart_dialog_click(ev.mouse.x, ev.mouse.y, clicked);
                    redraw_np = g_state.np_dlg.open;
                    redraw_main = true;
                }
            }
        }

        // Poll format dialog window
        if (g_state.fmt_dlg.open) {
            int dr = montauk::win_poll(g_state.fmt_dlg.win_id, &ev);
            if (dr > 0) {
                if (ev.type == 3) { // close
                    close_format_dialog();
                    redraw_main = true;
                } else if (ev.type == 0 && ev.key.pressed) {
                    handle_key(ev.key);
                    redraw_dlg = g_state.fmt_dlg.open;
                    redraw_main = true;
                } else if (ev.type == 1) {
                    bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
                    handle_format_dialog_click(ev.mouse.x, ev.mouse.y, clicked);
                    redraw_dlg = g_state.fmt_dlg.open;
                    redraw_main = true;
                }
            }
        }

        // Poll main window
        int r = win.poll(&ev);

        if (r < 0) break;
        if (r == 0) {
            // Even with no main event, dialog may have triggered redraws
            if (redraw_main) { render(pixels); win.present(); }
            if (redraw_dlg) { render_format_window(); montauk::win_present(g_state.fmt_dlg.win_id); }
            if (redraw_np) { render_newpart_window(); montauk::win_present(g_state.np_dlg.win_id); }
            if (!redraw_main && !redraw_dlg && !redraw_np) montauk::sleep_ms(16);
            continue;
        }

        if (ev.type == 3) break; // close

        // Resize
        if (ev.type == 2) {
            g_win_w = win.width;
            g_win_h = win.height;
            pixels = win.pixels;
            clamp_scroll();
            redraw_main = true;
        }

        // Keyboard
        if (ev.type == 0 && ev.key.pressed) {
            if (!handle_key(ev.key) && ev.key.scancode == 0x01)
                break; // Escape quits when not in dialog
            redraw_main = true;
        }

        // Mouse
        if (ev.type == 1) {
            redraw_main |= handle_mouse(ev);
        }

        if (redraw_main) { render(pixels); win.present(); }
        if (redraw_dlg) { render_format_window(); montauk::win_present(g_state.fmt_dlg.win_id); }
        if (redraw_np) { render_newpart_window(); montauk::win_present(g_state.np_dlg.win_id); }
    }

    close_newpart_dialog();
    close_format_dialog();
    win.destroy();
    montauk::exit(0);
}
