#include "btcli.h"

void
usage_stat(void)
{
    printf(
        "Display stats for active torrents.\n"
        "\n"
        "Usage: stat [-i] [-w seconds] [torrent ...]\n"
        "\n"
        "Arguments:\n"
        "torrent ...\n"
        "\tOnly display stats for the given torrents.\n"
        "\n"
        "Options:\n"
        "-i\n"
        "\tDisplay individual lines for each torrent.\n"
        "\n"
        "-n\n"
        "\tDisplay the name of each torrent. Implies '-i'.\n"
        "\n"
        "-w n\n"
        "\tDisplay stats every n seconds.\n"
        "\n");
    exit(1);
}

struct btstat {
    unsigned num;
    enum ipc_tstate state;
    unsigned peers, tr_good;
    long long content_got, content_size, downloaded, uploaded, rate_up,
        rate_down, tot_up;
    uint32_t pieces_seen, torrent_pieces;
};

struct cbarg {
    int individual, names;
    struct btstat tot;
};

static enum ipc_tval stkeys[] = {
    IPC_TVAL_STATE,
    IPC_TVAL_NUM,
    IPC_TVAL_NAME,
    IPC_TVAL_PCOUNT,
    IPC_TVAL_TRGOOD,
    IPC_TVAL_PCCOUNT,
    IPC_TVAL_PCSEEN,
    IPC_TVAL_SESSUP,
    IPC_TVAL_SESSDWN,
    IPC_TVAL_TOTUP,
    IPC_TVAL_RATEUP,
    IPC_TVAL_RATEDWN,
    IPC_TVAL_CGOT,
    IPC_TVAL_CSIZE
};

static size_t nstkeys = sizeof(stkeys) / sizeof(stkeys[0]);

static void
print_stat(struct btstat *st)
{
    print_percent(st->content_got, st->content_size);
    print_size(st->downloaded);
    print_rate(st->rate_down);
    print_size(st->uploaded);
    print_rate(st->rate_up);
    print_ratio(st->tot_up, st->content_size);
    printf("%4u ", st->peers);
    print_percent(st->pieces_seen, st->torrent_pieces);
    printf("%3u", st->tr_good);
    printf("\n");
}

static void
stat_cb(int obji, enum ipc_err objerr, struct ipc_get_res *res, void *arg)
{
    struct cbarg *cba = arg;
    struct btstat st, *tot = &cba->tot;
    if (objerr != IPC_OK || res[IPC_TVAL_STATE].v.num == IPC_TSTATE_INACTIVE)
        return;
    bzero(&st, sizeof(st));
    st.state = res[IPC_TVAL_STATE].v.num;
    st.num = res[IPC_TVAL_NUM].v.num;
    tot->torrent_pieces += (st.torrent_pieces = res[IPC_TVAL_PCCOUNT].v.num);
    tot->pieces_seen += (st.pieces_seen = res[IPC_TVAL_PCSEEN].v.num);
    tot->content_got += (st.content_got = res[IPC_TVAL_CGOT].v.num);
    tot->content_size += (st.content_size = res[IPC_TVAL_CSIZE].v.num);
    tot->downloaded += (st.downloaded = res[IPC_TVAL_SESSDWN].v.num);
    tot->uploaded += (st.uploaded = res[IPC_TVAL_SESSUP].v.num);
    tot->rate_down += (st.rate_down = res[IPC_TVAL_RATEDWN].v.num);
    tot->rate_up += (st.rate_up = res[IPC_TVAL_RATEUP].v.num);
    tot->peers += (st.peers = res[IPC_TVAL_PCOUNT].v.num);
    tot->tot_up += (st.tot_up = res[IPC_TVAL_TOTUP].v.num);
    tot->tr_good += (st.tr_good = res[IPC_TVAL_TRGOOD].v.num);
    if (cba->individual) {
        if (cba->names)
            printf("%.*s\n", (int)res[IPC_TVAL_NAME].v.str.l,
                res[IPC_TVAL_NAME].v.str.p);
        printf("%4u %c. ", st.num, tstate_char(st.state));
        print_stat(&st);
    }
}

static void
do_stat(int individual, int names, int seconds, struct ipc_torrent *tps,
    int ntps)
{
    enum ipc_err err;
    struct cbarg cba;
    int header = 1;
    if (names)
        individual = 1;
    cba.individual = individual;
    cba.names = names;
again:
    header--;
    if (header == 0) {
        if (individual) {
            header = 1;
            printf(" NUM ST ");
        } else
            header = 20;
        printf("  HAVE   DLOAD      RTDWN   ULOAD       RTUP   RATIO CONN"
            "  AVAIL  TR\n");
    }

    bzero(&cba.tot, sizeof(cba.tot));
    cba.tot.state = IPC_TSTATE_INACTIVE;
    if (tps == NULL)
        err = btpd_tget_wc(ipc, IPC_TWC_ACTIVE, stkeys, nstkeys,
            stat_cb, &cba);
    else
        err = btpd_tget(ipc, tps, ntps, stkeys, nstkeys, stat_cb, &cba);
    if (err != IPC_OK)
        diemsg("command failed (%s).\n", ipc_strerror(err));
    if (names)
        printf("-------\n");
    if (individual)
        printf("        ");
    print_stat(&cba.tot);
    if (seconds > 0) {
        sleep(seconds);
        goto again;
    }
}

static struct option stat_opts [] = {
    { "help", no_argument, NULL, 'H' },
    {NULL, 0, NULL, 0}
};

void
cmd_stat(int argc, char **argv)
{
    int ch;
    int wflag = 0, iflag = 0, nflag = 0, seconds = 0;
    struct ipc_torrent *tps = NULL;
    int ntps = 0;
    char *endptr;
    while ((ch = getopt_long(argc, argv, "inw:", stat_opts, NULL)) != -1) {
        switch (ch) {
        case 'i':
            iflag = 1;
            break;
        case 'n':
            nflag = 1;
            break;
        case 'w':
            wflag = 1;
            seconds = strtol(optarg, &endptr, 10);
            if (*endptr != '\0' || seconds < 1)
                usage_stat();
            break;
        default:
            usage_stat();
        }
    }
    argc -= optind;
    argv += optind;

    if (argc > 0) {
        tps = malloc(argc * sizeof(*tps));
        for (int i = 0; i < argc; i++) {
            if (torrent_spec(argv[i], &tps[ntps]))
                ntps++;
            else
                exit(1);

        }
    }
    btpd_connect();
    do_stat(iflag, nflag, seconds, tps, ntps);
}