#include "ui.h"

static const char *themes[][3] = {
    {"\033[38;5;51m", "\033[48;5;25m\033[38;5;15m", "\033[38;5;39m"},
    {"\033[38;5;46m", "\033[48;5;22m\033[38;5;15m", "\033[38;5;120m"},
    {"\033[38;5;214m", "\033[48;5;52m\033[38;5;15m", "\033[38;5;223m"}
};

static void ansi_home(void){ printf("\033[H"); }
static void ansi_clear(void){ printf("\033[2J"); }
static void ansi_hide_cursor(void){ printf("\033[?25l"); }
static void ansi_show_cursor(void){ printf("\033[?25h"); }
static void ansi_alt_on(void){ printf("\033[?1049h"); }
static void ansi_alt_off(void){ printf("\033[?1049l"); }
static void ansi_reset(void){ printf("\033[0m"); }
static void move_to(int y,int x){ printf("\033[%d;%dH", y, x); }

static void print_clip(int width, const char *text) {
    char buf[1024];
    snprintf(buf, sizeof(buf), "%.*s", width, text ? text : "");
    printf("%-*s", width, buf);
}

static void draw_hline(int y, int x, int w, const char *col) {
    move_to(y, x); printf("%s", col);
    for (int i = 0; i < w; i++) printf("═");
    ansi_reset();
}

static void raw_enable(ui_state_t *ui) {
#ifdef _WIN32
    HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
    DWORD mode = 0;
    GetConsoleMode(hin, &mode);
    mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
    mode |= ENABLE_WINDOW_INPUT;
    SetConsoleMode(hin, mode);
#else
    tcgetattr(STDIN_FILENO, &ui->orig_termios);
    struct termios t = ui->orig_termios;
    t.c_lflag &= ~(ICANON | ECHO);
    t.c_cc[VMIN] = 0;
    t.c_cc[VTIME] = 1;
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
#endif
}

void ui_init(ui_state_t *ui, const bookmark_t *initial, int bbs_mode) {
    memset(ui, 0, sizeof(*ui));
    ui->use_alt = 1;
    ui->running = 1;
    ui->show_users = 1;
    ui->dirty_frame = ui->dirty_input = ui->dirty_footer = 1;
    ui->current = *initial;
    ui->bbs_mode = bbs_mode;
    raw_enable(ui);
    if (ui->use_alt) ansi_alt_on();
    ansi_hide_cursor();
    ansi_clear();
    ansi_home();
    fflush(stdout);
}

void ui_shutdown(ui_state_t *ui) {
    ansi_reset();
    ansi_show_cursor();
    if (ui->use_alt) ansi_alt_off();
#ifndef _WIN32
    tcsetattr(STDIN_FILENO, TCSANOW, &ui->orig_termios);
#endif
    fflush(stdout);
}

int ui_read_key(void) {
#ifdef _WIN32
    int c = _getch();
    if (c == 0 || c == 224) {
        int e = _getch();
        switch (e) {
            case 72: return KEY_UP;
            case 80: return KEY_DOWN;
            case 75: return KEY_LEFT;
            case 77: return KEY_RIGHT;
            case 73: return KEY_PGUP;
            case 81: return KEY_PGDN;
            case 83: return KEY_DELETE;
        }
        return KEY_NONE;
    }
    if (c == 13) return KEY_ENTER;
    if (c == 8) return KEY_BACKSPACE;
    if (c == 9) return KEY_TAB;
    return c;
#else
    unsigned char c;
    if (read(STDIN_FILENO, &c, 1) <= 0) return KEY_NONE;
    if (c == '\n' || c == '\r') return KEY_ENTER;
    if (c == 127 || c == '\b') return KEY_BACKSPACE;
    if (c == 9) return KEY_TAB;
    if (c == 27) {
        unsigned char s[3] = {0,0,0};
        if (read(STDIN_FILENO, &s[0], 1) <= 0) return KEY_ESC;
        if (read(STDIN_FILENO, &s[1], 1) <= 0) return KEY_ESC;
        if (s[0] == '[') {
            switch (s[1]) {
                case 'A': return KEY_UP;
                case 'B': return KEY_DOWN;
                case 'C': return KEY_RIGHT;
                case 'D': return KEY_LEFT;
                case '5': read(STDIN_FILENO,&s[2],1); return KEY_PGUP;
                case '6': read(STDIN_FILENO,&s[2],1); return KEY_PGDN;
                case '3': read(STDIN_FILENO,&s[2],1); return KEY_DELETE;
            }
        } else if (s[0] == 'O') {
            /* VT100/xterm SS3: F1=\x1bOP F2=\x1bOQ F3=\x1bOR F4=\x1bOS */
            switch (s[1]) {
                case 'P': return KEY_F1;
                case 'Q': return KEY_F2;
                case 'R': return KEY_F3;
                case 'S': return KEY_F4;
            }
        }
        return KEY_ESC;
    }
    return c;
#endif
}

static void draw_frame(ui_state_t *ui, irc_client_t *irc, int startup) {
    screen_size_t ss = get_screen_size();
    const char *border = themes[ui->theme][0];
    const char *header = themes[ui->theme][1];
    ansi_home();
    printf("%s", header);
    print_clip(ss.width, startup ? " A-Net IRC Client 6.0 ║ Startup Manager ║ GUI + BBS Door Mode " : " A-Net IRC Client 6.0 ║ Chat View ");
    ansi_reset();
    int y1 = 2, y2 = ss.height - 2;
    int split = startup ? ss.width / 2 : (ui->show_users ? ss.width - 22 : ss.width);
    move_to(y1,1); printf("%s╔", border); for (int i=2;i<split;i++) printf("═");
    if (startup || ui->show_users) { printf("╦"); for (int i=split+1;i<ss.width;i++) printf("═"); }
    else { for (int i=split;i<ss.width;i++) printf("═"); }
    printf("╗");
    for (int y = y1 + 1; y < y2; y++) {
        move_to(y,1); printf("%s║", border);
        move_to(y,split); if (startup || ui->show_users) printf("%s║", border);
        move_to(y,ss.width); printf("%s║", border);
    }
    move_to(y2,1); printf("%s╚", border); for (int i=2;i<split;i++) printf("═");
    if (startup || ui->show_users) { printf("╩"); for (int i=split+1;i<ss.width;i++) printf("═"); }
    else { for (int i=split;i<ss.width;i++) printf("═"); }
    printf("╝"); ansi_reset();
    ui->dirty_frame = 0;
}

static void draw_startup(ui_state_t *ui, config_t *cfg) {
    screen_size_t ss = get_screen_size();
    int split = ss.width / 2;
    const char *accent = themes[ui->theme][2];
    move_to(3,3); printf("%sCONNECT FORM", accent); ansi_reset();
    const char *labels[] = {"Label", "Server", "Port", "Nick", "Channel", "TLS", "Password"};
    char vals[7][128];
    snprintf(vals[0], sizeof(vals[0]), "%s", ui->current.label);
    snprintf(vals[1], sizeof(vals[1]), "%s", ui->current.server);
    snprintf(vals[2], sizeof(vals[2]), "%d", ui->current.port);
    snprintf(vals[3], sizeof(vals[3]), "%s", ui->current.nick);
    snprintf(vals[4], sizeof(vals[4]), "%s", ui->current.channel);
    snprintf(vals[5], sizeof(vals[5]), "%s", ui->current.tls ? "Yes" : "No");
    /* mask password display */
    size_t pwlen = strlen(ui->current.password);
    memset(vals[6], '*', pwlen < sizeof(vals[6]) - 1 ? pwlen : sizeof(vals[6]) - 2);
    vals[6][pwlen < sizeof(vals[6]) - 1 ? pwlen : sizeof(vals[6]) - 2] = 0;
    if (!pwlen) snprintf(vals[6], sizeof(vals[6]), "(none — SASL disabled)");
    for (int i = 0; i < 7; i++) {
        move_to(5 + i * 2, 3);
        if (ui->startup_field == i) printf("%s", themes[ui->theme][1]);
        print_clip(8, labels[i]); ansi_reset();
        move_to(5 + i * 2, 11); printf("%s", accent); print_clip(split - 14, vals[i]); ansi_reset();
    }
    move_to(3, split + 2); printf("%sADDRESS BOOK", accent); ansi_reset();
    int rows = ss.height - 8;
    for (int i = 0; i < rows; i++) {
        move_to(5 + i, split + 2); print_clip(ss.width - split - 3, "");
    }
    for (int i = 0; i < cfg->count && i < rows; i++) {
        char line[256];
        snprintf(line, sizeof(line), "%d. %-18s %-24s", i + 1, cfg->items[i].label, cfg->items[i].server);
        move_to(5 + i, split + 2);
        if (ui->startup_bookmark == i) printf("%s", themes[ui->theme][1]);
        print_clip(ss.width - split - 3, line); ansi_reset();
    }
    move_to(ss.height - 3, 3);
    print_clip(ss.width - 4, "Enter edit ║ L load ║ Space toggle TLS ║ C connect ║ T theme ║ A save ║ D delete ║ Esc quit");
    move_to(ss.height, 2);
    print_clip(ss.width - 2, ui->bbs_mode ? "BBS terminal mode enabled." : "Normal terminal mode. For BBS: run with --bbs or place DOOR.SYS / DOOR32.SYS in working dir.");
    fflush(stdout);
}

static void draw_chat(ui_state_t *ui, irc_client_t *irc) {
    screen_size_t ss = get_screen_size();
    int left_w = ui->show_users ? ss.width - 23 : ss.width - 1;
    int col_w = left_w - 4;
    int top = 3, bottom = ss.height - 4;
    int max_rows = bottom - top + 1;

    /* Word-wrap all raw lines into display rows (BSS — not stack) */
#define WRAP_BUF 2048
    static char wrap_text[WRAP_BUF][256];
    static int  wrap_n;
    wrap_n = 0;

    for (int i = 0; i < irc->line_count && wrap_n < WRAP_BUF; i++) {
        chat_line_t *ln = &irc->lines[i];
        char ts[16]; format_time(ts, sizeof(ts), ln->ts);
        char prefix[96];
        if (ln->status) snprintf(prefix, sizeof(prefix), "[%s] * ", ts);
        else            snprintf(prefix, sizeof(prefix), "[%s] <%s> ", ts, ln->nick);
        int plen = (int)strlen(prefix);
        int row_avail = col_w - plen;
        if (row_avail < 8) row_avail = 8;

        const char *src = ln->text;
        int first = 1;
        while (wrap_n < WRAP_BUF) {
            char row[256];
            if (first) {
                snprintf(row, sizeof(row), "%s", prefix);
                first = 0;
            } else {
                memset(row, ' ', plen < (int)sizeof(row)-1 ? plen : (int)sizeof(row)-2);
                row[plen < (int)sizeof(row)-1 ? plen : (int)sizeof(row)-2] = '\0';
            }
            int src_len = (int)strlen(src);
            if (src_len == 0) break;

            int dst_used = (int)strlen(row);
            int space = (int)sizeof(row) - dst_used - 1;
            int cut;
            if (src_len <= row_avail && src_len <= space) {
                cut = src_len;
            } else {
                cut = row_avail < space ? row_avail : space;
                /* walk back to a word boundary */
                int br = -1;
                for (int j = cut - 1; j > 0; j--) {
                    if (src[j] == ' ') { br = j; break; }
                }
                if (br > 0) cut = br;
            }
            strncat(row, src, cut);
            src += cut;
            while (*src == ' ') src++;

            strncpy(wrap_text[wrap_n], row, 255);
            wrap_text[wrap_n][255] = '\0';
            wrap_n++;
        }
    }

    /* Clear chat area */
    for (int y = top; y <= bottom; y++) {
        move_to(y, 3); print_clip(col_w, "");
    }
    move_to(3,3); printf("%sCHAT", themes[ui->theme][2]); ansi_reset();

    /* Newest at bottom; scroll offsets pull up from the end */
    int start = wrap_n - max_rows - ui->chat_scroll;
    if (start < 0) start = 0;
    for (int r = 0; r < max_rows; r++) {
        int idx = start + r;
        if (idx >= wrap_n) break;
        move_to(top + r, 3);
        print_clip(col_w, wrap_text[idx]);
    }

    if (ui->show_users) {
        move_to(3, ss.width - 20); printf("%sUSERS", themes[ui->theme][2]); ansi_reset();
        for (int y = top; y <= bottom; y++) { move_to(y, ss.width - 20); print_clip(17, ""); }
        for (int i = 0; i < irc->user_count && i < max_rows; i++) {
            move_to(top + i, ss.width - 20); print_clip(17, irc->users[i]);
        }
    }
    irc->dirty_chat = irc->dirty_users = 0;
}

static void draw_status(ui_state_t *ui, irc_client_t *irc) {
    screen_size_t ss = get_screen_size();
    move_to(ss.height - 2, 1); printf("%s", themes[ui->theme][1]);
    char line[512];
    snprintf(line, sizeof(line), " %s:%d ║ %s ║ @%s ║ %s ║ PgUp/PgDn scroll ║ /help ",
        irc->server[0] ? irc->server : ui->current.server,
        irc->port ? irc->port : ui->current.port,
        irc->channel[0] ? irc->channel : ui->current.channel,
        irc->nick[0] ? irc->nick : ui->current.nick,
        irc->connected ? "CONNECTED" : "OFFLINE");
    print_clip(ss.width, line);
    ansi_reset();
    move_to(ss.height - 1, 1); print_clip(ss.width, "");
    move_to(ss.height - 1, 1); printf(">%s", ui->input);
    ui->dirty_input = ui->dirty_footer = 0; irc->dirty_status = 0;
    fflush(stdout);
}

static void startup_edit_field(ui_state_t *ui) {
    const char *prompt = "";
    char *dest = NULL;
    char buf[128] = {0};
    int is_num = 0;
    int is_password = 0;
    switch (ui->startup_field) {
        case 0: prompt = "Label";    dest = ui->current.label;    break;
        case 1: prompt = "Server";   dest = ui->current.server;   break;
        case 2: prompt = "Port";     is_num = 1; snprintf(buf, sizeof(buf), "%d", ui->current.port); break;
        case 3: prompt = "Nick";     dest = ui->current.nick;     break;
        case 4: prompt = "Channel";  dest = ui->current.channel;  break;
        case 5: ui->current.tls = !ui->current.tls; return;
        case 6: prompt = "Password"; dest = ui->current.password; is_password = 1; break;
    }
    if (!is_num) safe_strcpy(buf, dest, sizeof(buf));
    screen_size_t ss = get_screen_size();
    size_t pos = strlen(buf);
#define REDRAW_EDIT() do { \
        char _disp[sizeof(buf)]; \
        if (is_password) { memset(_disp,'*',pos); _disp[pos]=0; } \
        else safe_strcpy(_disp, buf, sizeof(_disp)); \
        move_to(ss.height-1,1); print_clip(ss.width,""); \
        move_to(ss.height-1,1); printf("%s: %s", prompt, _disp); fflush(stdout); \
    } while(0)
    REDRAW_EDIT();
    while (1) {
        move_to(ss.height - 1, (int)strlen(prompt) + 4 + (int)pos);
        int k = ui_read_key();
        if (k == KEY_ENTER) break;
        if (k == KEY_ESC) { return; }
        if (k == KEY_BACKSPACE && pos) buf[--pos] = 0;
        else if (k >= 32 && k < 127 && pos < sizeof(buf)-1) { buf[pos++] = (char)k; buf[pos]=0; }
        REDRAW_EDIT();
    }
#undef REDRAW_EDIT
    if (is_num) ui->current.port = atoi(buf);
    else safe_strcpy(dest, buf, 128);
    ui->dirty_frame = 1;
}

static void handle_startup(ui_state_t *ui, config_t *cfg, irc_client_t *irc, int key) {
    if (key == KEY_UP || key == 'k') { if (ui->startup_field > 0) ui->startup_field--; ui->dirty_frame = 1; }
    else if (key == KEY_DOWN || key == 'j') { if (ui->startup_field < 6) ui->startup_field++; ui->dirty_frame = 1; }
    else if (key == KEY_LEFT || key == 'p') { if (ui->startup_bookmark > 0) ui->startup_bookmark--; ui->dirty_frame = 1; }
    else if (key == KEY_RIGHT || key == 'n') { if (ui->startup_bookmark < cfg->count - 1) ui->startup_bookmark++; ui->dirty_frame = 1; }
    else if (key == ' ' && ui->startup_field == 5) { ui->current.tls = !ui->current.tls; ui->dirty_frame = 1; }
    else if (key == ' ' && ui->startup_field == 6) startup_edit_field(ui);
    else if (key == KEY_ENTER) startup_edit_field(ui);
    else if (key == 'l' || key == 'L') { if (cfg->count) ui->current = cfg->items[ui->startup_bookmark]; ui->dirty_frame = 1; }
    else if (key == 't' || key == 'T') { ui->theme = (ui->theme + 1) % 3; ui->dirty_frame = 1; }
    else if (key == 'a' || key == 'A') { config_add_or_update(cfg, &ui->current); config_save(cfg); ui->dirty_frame = 1; }
    else if (key == 'd' || key == 'D') { config_delete(cfg, ui->startup_bookmark); config_save(cfg); if (ui->startup_bookmark >= cfg->count) ui->startup_bookmark = cfg->count - 1; ui->dirty_frame = 1; }
    else if (key == 'c' || key == 'C') {
        if (irc_connect_server(irc, ui->current.server, ui->current.port, ui->current.nick, ui->current.channel, ui->current.tls, ui->current.password) == 0) ui->startup_mode = 0;
        ui->dirty_frame = ui->dirty_input = ui->dirty_footer = 1;
    }
    else if (key == KEY_ESC || key == 'q' || key == 'Q') ui->running = 0;
}

static void handle_chat(ui_state_t *ui, irc_client_t *irc, int key) {
    if (key == KEY_PGUP) { ui->chat_scroll += 5; irc->dirty_chat = 1; }
    else if (key == KEY_PGDN) { ui->chat_scroll -= 5; if (ui->chat_scroll < 0) ui->chat_scroll = 0; irc->dirty_chat = 1; }
    else if (key == KEY_ESC) ui->startup_mode = 1, ui->dirty_frame = 1;
    else if (key == KEY_BACKSPACE) { if (ui->input_pos > 0) ui->input[--ui->input_pos] = 0; ui->dirty_input = 1; }
    else if (key == KEY_ENTER) {
        if (ui->input[0]) irc_handle_command(irc, ui->input);
        ui->input[0] = 0; ui->input_pos = 0; ui->dirty_input = 1;
    } else if (key == KEY_TAB || key == KEY_F2) {
        ui->show_users = !ui->show_users; ui->dirty_frame = 1;
    } else if (key >= 32 && key < 127 && ui->input_pos < UI_MAX_INPUT - 1) {
        ui->input[ui->input_pos++] = (char)key;
        ui->input[ui->input_pos] = 0;
        ui->dirty_input = 1;
    }
}

void ui_run(ui_state_t *ui, config_t *cfg, irc_client_t *irc) {
    ui->startup_mode = 1;
    while (ui->running && !irc->should_quit) {
        if (ui->dirty_frame) {
            ansi_clear();
            draw_frame(ui, irc, ui->startup_mode);
            if (ui->startup_mode) draw_startup(ui, cfg);
            else draw_chat(ui, irc);
            draw_status(ui, irc);
        } else {
            if (!ui->startup_mode && (irc->dirty_chat || irc->dirty_users)) draw_chat(ui, irc);
            if (ui->dirty_input || ui->dirty_footer || irc->dirty_status) draw_status(ui, irc);
        }
        int k = ui_read_key();
        if (k != KEY_NONE) {
            if (ui->startup_mode) handle_startup(ui, cfg, irc, k);
            else handle_chat(ui, irc, k);
        }
        msleep(15);
    }
}
