#include "ui.h"
#include "../util.h"

#include <curses.h>
#include <string.h>

#define FOOTER_H 2

static const char *TAB_LABEL[DETAIL_TAB_COUNT] = {"General", "Files", "Peers", "Trackers"};

static void ensure_loaded(AppState *st)
{
    if (st->detail_loaded)
        return;
    char err[256];
    if (torrent_get_detail(st->rpc, st->detail_id, &st->detail, err, sizeof(err)) == 0) {
        st->detail_loaded = 1;
    } else {
        ui_set_status(st, "Error: %s", err);
        st->view = VIEW_LIST;
    }
}

static void draw_title(AppState *st, int cols)
{
    attron(COLOR_PAIR(CP_HEADER) | A_BOLD);
    mvhline(0, 0, ' ', cols);
    mvprintw(0, 0, " %-*.*s", cols - 1, cols - 1,
              st->detail_loaded ? st->detail.info.name : "");
    attroff(COLOR_PAIR(CP_HEADER) | A_BOLD);
}

static void draw_general(WINDOW *win, AppState *st, int top)
{
    TorrentDetail *d = &st->detail;
    char sizebuf[24], donebuf[24], downbuf[24], upbuf[24], addedbuf[32], createdbuf[32], donedatebuf[32];
    fmt_size(d->info.total_size, sizebuf, sizeof(sizebuf));
    fmt_size(d->info.total_size - d->info.left_until_done, donebuf, sizeof(donebuf));
    fmt_speed(d->info.rate_download, downbuf, sizeof(downbuf));
    fmt_speed(d->info.rate_upload, upbuf, sizeof(upbuf));
    fmt_time(d->date_added, addedbuf, sizeof(addedbuf));
    fmt_time(d->date_created, createdbuf, sizeof(createdbuf));
    fmt_time(d->done_date, donedatebuf, sizeof(donedatebuf));

    int y = top;
    mvwprintw(win, y++, 2, "Hash:            %s", d->info.hash);
    mvwprintw(win, y++, 2, "Size:            %s (%s done)", sizebuf, donebuf);
    mvwprintw(win, y++, 2, "Progress:        %.1f%%", d->info.percent_done * 100.0);
    mvwprintw(win, y++, 2, "Speed:           \xe2\x86\x93 %s   \xe2\x86\x91 %s", downbuf, upbuf);
    mvwprintw(win, y++, 2, "Ratio:           %.2f", d->info.upload_ratio);
    mvwprintw(win, y++, 2, "Peers:           %d connected (%d sending, %d receiving)",
              d->info.peers_connected, d->info.peers_sending_to_us, d->info.peers_getting_from_us);
    mvwprintw(win, y++, 2, "Down limit:      %s", d->download_limited ? "active" : "unlimited");
    mvwprintw(win, y++, 2, "Up limit:        %s", d->upload_limited ? "active" : "unlimited");
    mvwprintw(win, y++, 2, "Folder:          %s", d->info.download_dir);
    mvwprintw(win, y++, 2, "Added:           %s", addedbuf);
    mvwprintw(win, y++, 2, "Created:         %s", createdbuf);
    mvwprintw(win, y++, 2, "Completed:       %s", donedatebuf);
    if (d->info.error)
        mvwprintw(win, y++, 2, "Error:           %s", d->info.error_string);
}

static void draw_files(WINDOW *win, AppState *st, int top, int rows, int cols)
{
    TorrentDetail *d = &st->detail;
    if (d->file_count == 0) {
        mvwprintw(win, top, 2, "(no file information)");
        return;
    }
    if (st->detail_cursor >= (int)d->file_count)
        st->detail_cursor = (int)d->file_count - 1;
    if (st->detail_cursor < 0)
        st->detail_cursor = 0;

    int vis = rows;
    int first = 0;
    if (st->detail_cursor >= vis)
        first = st->detail_cursor - vis + 1;

    int name_w = cols - 36 > 10 ? cols - 36 : 10;
    for (int row = 0; row < vis; row++) {
        int i = first + row;
        if (i >= (int)d->file_count)
            break;
        TorrentFile *f = &d->files[i];
        char sizebuf[24];
        fmt_size(f->length, sizebuf, sizeof(sizebuf));
        double pct = f->length > 0 ? (100.0 * (double)f->bytes_completed / (double)f->length) : 100.0;
        const char *prio = f->priority > 0 ? "High" : (f->priority < 0 ? "Low" : "Normal");

        int attr = (i == st->detail_cursor) ? (COLOR_PAIR(CP_SELROW) | A_BOLD) : 0;
        mvwhline(win, top + row, 2, ' ', getmaxx(win) - 4);
        wattron(win, attr);
        mvwprintw(win, top + row, 2, "%c %-*.*s %9s %5.1f%% %-7s %s",
                  f->wanted ? ' ' : '-', name_w, name_w, f->name, sizebuf, pct, prio,
                  f->wanted ? "" : "(skipped)");
        wattroff(win, attr);
    }
}

static void draw_peers(WINDOW *win, AppState *st, int top, int rows)
{
    TorrentDetail *d = &st->detail;
    if (d->peer_count == 0) {
        mvwprintw(win, top, 2, "(no connected peers)");
        return;
    }
    for (int row = 0; row < rows; row++) {
        size_t i = (size_t)row;
        if (i >= d->peer_count)
            break;
        TorrentPeer *p = &d->peers[i];
        char downbuf[24], upbuf[24];
        fmt_speed(p->rate_to_client, downbuf, sizeof(downbuf));
        fmt_speed(p->rate_to_peer, upbuf, sizeof(upbuf));
        mvwprintw(win, top + row, 2, "%-22s %-20s %5.1f%% %10s %10s %-8s",
                  p->address, p->client_name, p->progress * 100.0, downbuf, upbuf, p->flags);
    }
}

static void draw_trackers(WINDOW *win, AppState *st, int top, int rows, int cols)
{
    TorrentDetail *d = &st->detail;
    if (d->tracker_count == 0) {
        mvwprintw(win, top, 2, "(no trackers)");
        return;
    }
    int url_w = cols - 22 > 10 ? cols - 22 : 10;
    for (int row = 0; row < rows; row++) {
        size_t i = (size_t)row;
        if (i >= d->tracker_count)
            break;
        TorrentTracker *t = &d->trackers[i];
        mvwprintw(win, top + row, 2, "[%d] %-*.*s S:%d L:%d", t->tier, url_w, url_w, t->announce,
                  t->seeder_count, t->leecher_count);
        if (t->last_announce_result[0])
            mvwprintw(win, top + row + 1, 6, "%.*s", cols - 7, t->last_announce_result);
    }
}

static const KeyHint DETAIL_HINTS[] = {
    {"Esc/q", "Back"}, {"Tab", "Next tab"}, {"1-4", "Select tab"},
    {"Space", "Select/skip (Files)"}, {"+/-", "Priority (Files)"},
};

void ui_detail_render(AppState *st)
{
    ensure_loaded(st);
    if (st->view != VIEW_DETAIL)
        return;

    int rows, cols;
    getmaxyx(stdscr, rows, cols);
    erase();
    draw_title(st, cols);

    int body_top = 1;
    int body_h = rows - FOOTER_H - 1 - body_top;
    if (body_h < 3)
        body_h = 3;

    WINDOW *win = derwin(stdscr, body_h, cols, body_top, 0);
    ui_box(win, body_h, cols);

    int x = 2;
    for (int i = 0; i < DETAIL_TAB_COUNT; i++) {
        int active = (i == (int)st->detail_tab);
        wattron(win, active ? (COLOR_PAIR(CP_SELROW) | A_BOLD) : (COLOR_PAIR(CP_ACCENT)));
        mvwprintw(win, 0, x, " %d:%s ", i + 1, TAB_LABEL[i]);
        wattroff(win, active ? (COLOR_PAIR(CP_SELROW) | A_BOLD) : (COLOR_PAIR(CP_ACCENT)));
        x += (int)strlen(TAB_LABEL[i]) + 5;
    }

    int content_top = 2;
    int content_rows = body_h - content_top - 1;
    if (content_rows < 1)
        content_rows = 1;

    switch (st->detail_tab) {
    case DETAIL_GENERAL:
        draw_general(win, st, content_top);
        break;
    case DETAIL_FILES:
        draw_files(win, st, content_top, content_rows, cols);
        break;
    case DETAIL_PEERS:
        draw_peers(win, st, content_top, content_rows);
        break;
    case DETAIL_TRACKERS:
        draw_trackers(win, st, content_top, content_rows, cols);
        break;
    default:
        break;
    }

    wnoutrefresh(win);
    delwin(win);

    ui_draw_footer(DETAIL_HINTS, sizeof(DETAIL_HINTS) / sizeof(DETAIL_HINTS[0]), FOOTER_H);
    doupdate();
}

void ui_detail_handle_key(AppState *st, int ch)
{
    switch (ch) {
    case 'q':
    case 27:
    case KEY_BACKSPACE:
    case 127:
        ui_close_detail(st);
        return;
    case '\t':
        st->detail_tab = (st->detail_tab + 1) % DETAIL_TAB_COUNT;
        st->detail_cursor = 0;
        return;
    case KEY_BTAB:
        st->detail_tab = (st->detail_tab + DETAIL_TAB_COUNT - 1) % DETAIL_TAB_COUNT;
        st->detail_cursor = 0;
        return;
    case '1':
        st->detail_tab = DETAIL_GENERAL;
        st->detail_cursor = 0;
        return;
    case '2':
        st->detail_tab = DETAIL_FILES;
        st->detail_cursor = 0;
        return;
    case '3':
        st->detail_tab = DETAIL_PEERS;
        st->detail_cursor = 0;
        return;
    case '4':
        st->detail_tab = DETAIL_TRACKERS;
        st->detail_cursor = 0;
        return;
    default:
        break;
    }

    if (st->detail_tab == DETAIL_FILES && st->detail_loaded) {
        TorrentDetail *d = &st->detail;
        switch (ch) {
        case KEY_UP:
        case 'k':
            if (st->detail_cursor > 0)
                st->detail_cursor--;
            break;
        case KEY_DOWN:
        case 'j':
            if ((size_t)(st->detail_cursor + 1) < d->file_count)
                st->detail_cursor++;
            break;
        case ' ': {
            if ((size_t)st->detail_cursor >= d->file_count)
                break;
            TorrentFile *f = &d->files[st->detail_cursor];
            int newwanted = !f->wanted;
            char err[256];
            if (torrent_set_file_wanted(st->rpc, st->detail_id, &st->detail_cursor, 1, newwanted,
                                         err, sizeof(err)) == 0) {
                f->wanted = newwanted;
            } else {
                ui_set_status(st, "Error: %s", err);
            }
            break;
        }
        case '+':
        case '-': {
            if ((size_t)st->detail_cursor >= d->file_count)
                break;
            TorrentFile *f = &d->files[st->detail_cursor];
            int prio = f->priority + (ch == '+' ? 1 : -1);
            if (prio > 1)
                prio = 1;
            if (prio < -1)
                prio = -1;
            char err[256];
            if (torrent_set_file_priority(st->rpc, st->detail_id, &st->detail_cursor, 1, prio,
                                           err, sizeof(err)) == 0) {
                f->priority = prio;
            } else {
                ui_set_status(st, "Error: %s", err);
            }
            break;
        }
        default:
            break;
        }
    }
}
