/*
    * app_terminal.cpp
    * MontaukOS Desktop - Terminal application with tab support
    * Copyright (c) 2026 Daniel Hammer
*/

#include "apps_common.hpp"

// ============================================================================
// Tab data model
// ============================================================================

static constexpr int TERM_TAB_BAR_H = 34;
static constexpr int TERM_MAX_TABS  = 8;
static constexpr int TERM_TAB_W     = 130;
static constexpr int TERM_TAB_GAP   = 4;
static constexpr int TERM_PLUS_W    = 28;
static constexpr int TERM_PLUS_PAD  = 8;

// Compute the x-offset that centers all tabs within bar_w
static int term_tabs_origin(int bar_w, int tab_count) {
    int total = tab_count * TERM_TAB_W + (tab_count - 1) * TERM_TAB_GAP;
    int x = (bar_w - total) / 2;
    if (x < 4) x = 4;
    return x;
}

struct TermTabState {
    TerminalState* tabs[TERM_MAX_TABS];
    int tab_count;
    int active_tab;
    int prev_active_tab;      // track tab switches to force redraw
    uint32_t* last_content;   // detect content buffer reallocation
    DesktopState* desktop;
};

// ============================================================================
// Tab helpers
// ============================================================================

static void term_free_tab(TerminalState* ts) {
    if (!ts) return;
    if (ts->child_pid > 0) montauk::kill(ts->child_pid);
    if (ts->cells) montauk::free(ts->cells);
    if (ts->alt_cells) montauk::free(ts->alt_cells);
    montauk::mfree(ts);
}

static TerminalState* term_create_tab(DesktopState* ds, int cols, int rows) {
    TerminalState* ts = (TerminalState*)montauk::malloc(sizeof(TerminalState));
    montauk::memset(ts, 0, sizeof(TerminalState));
    terminal_init(ts, cols, rows);
    ts->desktop = ds;
    return ts;
}

static void term_close_tab(TermTabState* tts, Window* win, int idx) {
    if (idx < 0 || idx >= tts->tab_count) return;

    term_free_tab(tts->tabs[idx]);

    // Shift remaining tabs left
    for (int i = idx; i < tts->tab_count - 1; i++)
        tts->tabs[i] = tts->tabs[i + 1];
    tts->tab_count--;
    tts->tabs[tts->tab_count] = nullptr;

    if (tts->tab_count == 0) {
        // Last tab closed — close the window
        DesktopState* ds = tts->desktop;
        if (ds) {
            for (int i = 0; i < ds->window_count; i++) {
                if (&ds->windows[i] == win) {
                    desktop_close_window(ds, i);
                    return;
                }
            }
        }
        return;
    }

    // Adjust active tab index
    if (idx == tts->active_tab) {
        // Closed the active tab: prefer left neighbor, fall back to right
        if (tts->active_tab > 0)
            tts->active_tab--;
        // else stays at 0 (right neighbor shifted here)
    } else if (tts->active_tab > idx) {
        tts->active_tab--;
    }
    if (tts->active_tab >= tts->tab_count)
        tts->active_tab = tts->tab_count - 1;

    // Mark new active tab dirty so it redraws
    tts->tabs[tts->active_tab]->dirty = true;
}

static void term_add_tab(TermTabState* tts, int cols, int rows) {
    if (tts->tab_count >= TERM_MAX_TABS) return;
    TerminalState* ts = term_create_tab(tts->desktop, cols, rows);
    tts->tabs[tts->tab_count] = ts;
    tts->active_tab = tts->tab_count;
    tts->tab_count++;
}

// ============================================================================
// Terminal callbacks
// ============================================================================

static void terminal_on_draw(Window* win, Framebuffer& fb) {
    TermTabState* tts = (TermTabState*)win->app_data;
    if (!tts || tts->tab_count == 0) return;
    if (tts->active_tab < 0 || tts->active_tab >= tts->tab_count)
        tts->active_tab = 0;

    Rect cr = win->content_rect();
    int term_h = cr.h - TERM_TAB_BAR_H;
    if (term_h < 1) term_h = 1;

    // Force full redraw when active tab changed
    if (tts->active_tab != tts->prev_active_tab) {
        tts->tabs[tts->active_tab]->dirty = true;
        tts->prev_active_tab = tts->active_tab;
    }

    // Force full redraw when content buffer was reallocated (resize/maximize)
    if (win->content != tts->last_content) {
        tts->tabs[tts->active_tab]->dirty = true;
        tts->last_content = win->content;
    }

    TerminalState* ts = tts->tabs[tts->active_tab];

    // Check if window was resized and update terminal grid
    int new_cols = cr.w / mono_cell_width();
    int new_rows = term_h / mono_cell_height();
    if (new_cols < 1) new_cols = 1;
    if (new_rows < 1) new_rows = 1;
    if (new_cols != ts->cols || new_rows != ts->rows) {
        for (int i = 0; i < tts->tab_count; i++)
            terminal_resize(tts->tabs[i], new_cols, new_rows);
    }

    // Only redraw when terminal content changed
    if (!ts->dirty) return;

    // ---- Draw tab bar ----
    // Bar is darker than TERM_BG; active tab matches TERM_BG to merge with content
    Canvas c(win->content, cr.w, cr.h);
    Color bar_bg = Color::from_hex(0x1C1C1C);
    c.fill_rect(0, 0, cr.w, TERM_TAB_BAR_H, bar_bg);

    int fh = system_font_height();
    int tab_x = term_tabs_origin(cr.w, tts->tab_count);

    for (int i = 0; i < tts->tab_count; i++) {
        bool active = (i == tts->active_tab);
        char label[16];
        snprintf(label, sizeof(label), "Tab %d", i + 1);

        if (active) {
            // Active tab: TERM_BG pill with squared-off bottom → merges into content
            int ty = 5;
            int th = TERM_TAB_BAR_H - ty;
            c.fill_rounded_rect(tab_x, ty, TERM_TAB_W, th, 6, colors::TERM_BG);
            c.fill_rect(tab_x, TERM_TAB_BAR_H - 6, TERM_TAB_W, 6, colors::TERM_BG);

            int text_y = ty + (th - fh) / 2;
            c.text(tab_x + 12, text_y, label, Color::from_hex(0xE0E0E0));
            c.text(tab_x + TERM_TAB_W - 20, text_y, "x", Color::from_hex(0x707070));
        } else {
            // Inactive tab: subtle rounded pill, sits above the bottom edge
            int ty = 7;
            int th = 22;
            c.fill_rounded_rect(tab_x, ty, TERM_TAB_W, th, 5, Color::from_hex(0x262626));

            int text_y = ty + (th - fh) / 2;
            c.text(tab_x + 12, text_y, label, Color::from_hex(0x6E6E6E));
            c.text(tab_x + TERM_TAB_W - 20, text_y, "x", Color::from_hex(0x444444));
        }

        tab_x += TERM_TAB_W + TERM_TAB_GAP;
    }

    // "+" button pinned to right edge
    if (tts->tab_count < TERM_MAX_TABS) {
        int plus_h = 22;
        int py = 7;
        int px = cr.w - TERM_PLUS_PAD - TERM_PLUS_W;
        c.fill_rounded_rect(px, py, TERM_PLUS_W, plus_h, 5, Color::from_hex(0x262626));
        int pw_text = text_width("+");
        c.text(px + (TERM_PLUS_W - pw_text) / 2, py + (plus_h - fh) / 2, "+",
               Color::from_hex(0x6E6E6E));
    }

    // ---- Render active terminal into offset buffer ----
    uint32_t* term_pixels = win->content + TERM_TAB_BAR_H * cr.w;
    terminal_render(ts, term_pixels, cr.w, term_h);

    // Draw scrollbar overlay when scrollback exists
    if (ts->scrollback_lines > 0 && !ts->alt_screen_active) {
        int cell_h = mono_cell_height();
        int total_rows = ts->scrollback_lines + ts->rows;
        int total_h = total_rows * cell_h;
        int view_h = term_h;
        if (total_h > view_h) {
            Canvas sc(term_pixels, cr.w, term_h);
            int sb_w = 4;
            int sb_x = cr.w - sb_w - 2;
            int thumb_h = (view_h * view_h) / total_h;
            if (thumb_h < 20) thumb_h = 20;
            int scroll_from_top = ts->scrollback_lines - ts->view_offset;
            int max_scroll = ts->scrollback_lines;
            int thumb_y = (max_scroll > 0)
                ? (scroll_from_top * (view_h - thumb_h)) / max_scroll : 0;
            sc.fill_rect(sb_x, 0, sb_w, view_h, Color::from_rgb(0x33, 0x33, 0x33));
            sc.fill_rect(sb_x, thumb_y, sb_w, thumb_h, Color::from_rgb(0x88, 0x88, 0x88));
        }
    }
}

static void terminal_on_mouse(Window* win, MouseEvent& ev) {
    TermTabState* tts = (TermTabState*)win->app_data;
    if (!tts || tts->tab_count == 0) return;

    // Scroll wheel: forward to active tab regardless of mouse position
    TerminalState* ts = tts->tabs[tts->active_tab];
    if (ev.scroll != 0 && !ts->alt_screen_active) {
        ts->view_offset += (ev.scroll < 0) ? 3 : -3;
        if (ts->view_offset < 0) ts->view_offset = 0;
        if (ts->view_offset > ts->scrollback_lines)
            ts->view_offset = ts->scrollback_lines;
        ts->dirty = true;
    }

    // Tab bar click handling
    if (!ev.left_pressed()) return;

    Rect cr = win->content_rect();
    int lx = ev.x - cr.x;
    int ly = ev.y - cr.y;

    if (ly >= TERM_TAB_BAR_H) return;  // click in terminal area, ignore

    int tab_x = term_tabs_origin(cr.w, tts->tab_count);
    for (int i = 0; i < tts->tab_count; i++) {
        if (lx >= tab_x && lx < tab_x + TERM_TAB_W) {
            // Check if click is on the close "x" (last 24px of tab)
            if (lx >= tab_x + TERM_TAB_W - 24) {
                term_close_tab(tts, win, i);
            } else if (i != tts->active_tab) {
                tts->active_tab = i;
                tts->tabs[i]->dirty = true;
            }
            return;
        }
        tab_x += TERM_TAB_W + TERM_TAB_GAP;
    }

    // "+" button pinned to right edge
    int px = cr.w - TERM_PLUS_PAD - TERM_PLUS_W;
    if (tts->tab_count < TERM_MAX_TABS && lx >= px && lx < px + TERM_PLUS_W) {
        int term_h = cr.h - TERM_TAB_BAR_H;
        int cols = cr.w / mono_cell_width();
        int rows = term_h / mono_cell_height();
        if (cols < 1) cols = 1;
        if (rows < 1) rows = 1;
        term_add_tab(tts, cols, rows);
    }
}

static void terminal_on_key(Window* win, const Montauk::KeyEvent& key) {
    TermTabState* tts = (TermTabState*)win->app_data;
    if (!tts || tts->tab_count == 0) return;

    // Ctrl+T: new tab (scancode 0x14 = 'T')
    if (key.ctrl && key.pressed && key.scancode == 0x14) {
        if (tts->tab_count < TERM_MAX_TABS) {
            Rect cr = win->content_rect();
            int term_h = cr.h - TERM_TAB_BAR_H;
            int cols = cr.w / mono_cell_width();
            int rows = term_h / mono_cell_height();
            if (cols < 1) cols = 1;
            if (rows < 1) rows = 1;
            term_add_tab(tts, cols, rows);
        }
        return;
    }

    // Ctrl+W: close current tab (scancode 0x11 = 'W')
    if (key.ctrl && key.pressed && key.scancode == 0x11) {
        term_close_tab(tts, win, tts->active_tab);
        return;
    }

    // Forward all other keys to active tab
    TerminalState* ts = tts->tabs[tts->active_tab];
    terminal_handle_key(ts, key);
}

static void terminal_on_close(Window* win) {
    TermTabState* tts = (TermTabState*)win->app_data;
    if (tts) {
        for (int i = 0; i < tts->tab_count; i++)
            term_free_tab(tts->tabs[i]);
        montauk::mfree(tts);
        win->app_data = nullptr;
    }
}

static void terminal_on_poll(Window* win) {
    TermTabState* tts = (TermTabState*)win->app_data;
    if (!tts) return;

    // Poll ALL tabs — background shells still produce output
    for (int i = tts->tab_count - 1; i >= 0; i--) {
        if (!terminal_poll(tts->tabs[i])) {
            // This tab's shell exited
            term_close_tab(tts, win, i);
            // term_close_tab may have closed the window (last tab).
            // desktop_close_window calls on_close which nulls app_data
            // before the window slot is recycled, so win is still valid here.
            if (!win->app_data) return;
        }
    }
}

// ============================================================================
// Terminal launcher
// ============================================================================

void open_terminal(DesktopState* ds) {
    int idx = desktop_create_window(ds, "Terminal", 200, 80, 648, 480);
    if (idx < 0) return;

    Window* win = &ds->windows[idx];
    Rect cr = win->content_rect();
    int term_h = cr.h - TERM_TAB_BAR_H;
    int cols = cr.w / mono_cell_width();
    int rows = term_h / mono_cell_height();
    if (cols < 1) cols = 1;
    if (rows < 1) rows = 1;

    TermTabState* tts = (TermTabState*)montauk::malloc(sizeof(TermTabState));
    montauk::memset(tts, 0, sizeof(TermTabState));
    tts->desktop = ds;
    tts->tabs[0] = term_create_tab(ds, cols, rows);
    tts->tab_count = 1;
    tts->active_tab = 0;
    tts->prev_active_tab = 0;
    tts->last_content = win->content;

    win->app_data = tts;
    win->on_draw = terminal_on_draw;
    win->on_mouse = terminal_on_mouse;
    win->on_key = terminal_on_key;
    win->on_close = terminal_on_close;
    win->on_poll = terminal_on_poll;
}
