/* This file was contributed by Suzanne Skinner and is copyrighted
   under the GNU General Public License. (C) 2002 Suzanne Skinner.

   The code contained in this file is free software; you can
   redistribute it and/or modify it under the terms of the GNU General
   Public License as published by the Free Software Foundation; either
   version 2, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/

#include <stdio.h>
#include <string.h>
#include <ncurses.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#include <time.h>

#include "dynstr.h"

#include "defines.h"
#include "colors.h"
#include "codes.h"
#include "scheck.h"
#include "nap.h"
#include "winio.h"
#include "sscr.h"
#include "lists.h"
#include "alias.h"
#include "status_line.h"
#include "dlul_screen.h"

extern upload_t    *up;
extern download_t  *down;

/* Command-line-related variables from winio.c */
extern cmds_t *ccmd;
extern char    cbuf[];
extern int     curr;
extern int     curx;

WINDOW *dlul_win;

static int     num_dl, num_ul;
static int     dl_room, ul_room;
static int     dl_first_disp      = 0;
static int     ul_first_disp      = 0;
static char    dlul_section       = 'd';
static int     dlul_cursor        = 0;
static bool    filename_in_status = FALSE;
static dynstr  field_buf          = NULL;

static dlul_field_info_type dlul_field_info[] = {
    { FLD_FILENAME,   0 },
    { FLD_USER,      10 },
    { FLD_PROGRESS,  18 }
};

static const char *dlul_help =
    "HELP - use <right> and <left> to scroll this information. "
    "<up> and <down> to move cursor. "
    "M-<up> and M-<down> to move between sections. "
    "d to delete task at cursor. <return> to retry download at cursor. "
    "R to retry all failed downloads. "
    "f to force immediate download at cursor. "
    "P to purge stopped tasks in the current section. "
    "q to return to main screen. "
    "F1-F3 or M-1 thru M-3 to switch between screens. "
    "<tab> to switch between results and dl/ul screens. "
    "<space> to enter a new search. "
    "'/' to goto main screen and start a command.";

void dlulscr(void)
{
    sock_t *sk;

    dlul_win = newwin("dlul", LINES, 0, 0, 0);
    wbkgdset(dlul_win, COLOR_PAIR(CPW));
    keypad(dlul_win, TRUE);
    sk = findsock("input");
    if (sk)
        sk->func = dlul_input;
    sl_set("Use F1 or 'q' to return to the main screen. 'h' for help.");
    filename_in_status = FALSE;
    dlul_refresh();
}

void enddlulscr(void)
{
    delwin(dlul_win);
}

void dlul_refresh(void)
{
    char        *heading = NULL;
    download_t  *dl;
    upload_t    *ul;
    int          divider_pos;
    int          i, num;
    int          field_lens_sum = 0;
    int          var_field_index = -1;
    bool         selected;
    const char  *fn_at_cursor = NULL;

    if (LINES < 5 || COLS < 60)
        return;
    werase(dlul_win);

    for (i=0; i < DLUL_NUM_FIELDS; i++) {
        if (dlul_field_info[i].len == 0)
            var_field_index = i;
        else
            field_lens_sum += dlul_field_info[i].len;
    }
    if (var_field_index >= 0)
        /* The '3*(blah)' part accounts for the " | " divider between fields */
        dlul_field_info[var_field_index].len = COLS - 3*(DLUL_NUM_FIELDS-1) -
            field_lens_sum;

    divider_pos = LINES/2;
    dl_room = divider_pos - 1;
    ul_room = LINES - divider_pos - 2;

    num_dl=0;
    list_forall (dl, down)
        num_dl++;
    num_ul=0;
    list_forall (ul, up)
        num_ul++;
    if (num_dl && !num_ul)
        dlul_section = 'd';
    else if (num_ul && !num_dl)
        dlul_section = 'u';

    if (dlul_cursor < 0)
        dlul_cursor = 0;
    if (dlul_section == 'd') {
        if (dlul_cursor >= num_dl)
            dlul_cursor = num_dl-1;
        if (dlul_cursor < dl_first_disp)
            dl_first_disp = dlul_cursor;
        else if (dl_first_disp + dl_room <= dlul_cursor)
            dl_first_disp = dlul_cursor - dl_room + 1;
    }
    else {
        if (dlul_cursor >= num_ul)
            dlul_cursor = num_ul-1;
        if (dlul_cursor < ul_first_disp)
            ul_first_disp = dlul_cursor;
        else if (ul_first_disp + ul_room <= dlul_cursor)
            ul_first_disp = dlul_cursor - ul_room + 1;
    }

    /*** Print "Downloads" header ***/
    msprintf(&heading, "Downloads (%d)", num_dl);
    wmove(dlul_win, 0, 0);
    wattron(dlul_win, COLOR_PAIR(CPWB));
    whline(dlul_win, ' ', COLS);
    wmove(dlul_win, 0, (COLS - strlen(heading)) / 2);
    waddstr(dlul_win, heading);
    wattroff(dlul_win, COLOR_PAIR(CPWB));

    /*** Print "Uploads" header ***/
    msprintf(&heading, "Uploads (%d)", num_ul);
    wmove(dlul_win, divider_pos, 0);
    wattron(dlul_win, COLOR_PAIR(CPWB));
    whline(dlul_win, ' ', COLS);
    wmove(dlul_win, divider_pos, (COLS - strlen(heading)) / 2);
    waddstr(dlul_win, heading);
    wattroff(dlul_win, COLOR_PAIR(CPWB));

    /*** Print Downloads ***/
    for (dl=down, num=0; num < dl_first_disp; dl = dl->next, num++) {}
    for (i=0; dl && i < dl_room; dl = dl->next, num++, i++) {
        selected = (dlul_section == 'd' && dlul_cursor == num);
        if (selected) {
            fn_at_cursor = dl->fn;
            wattron(dlul_win, A_REVERSE);
        }
        wmove(dlul_win, i+1, 0);
        dlul_print_download(num, dl);
        if (selected)
           wattroff(dlul_win, A_REVERSE);
    }

    /*** Print Uploads ***/
    for (ul=up, num=0; num < ul_first_disp; ul = ul->next, num++) {}
    for (i=0; ul && i < ul_room; ul = ul->next, num++, i++) {
        selected = (dlul_section == 'u' && dlul_cursor == num);
        if (selected) {
            fn_at_cursor = ul->fn;
            wattron(dlul_win, A_REVERSE);
        }
        wmove(dlul_win, divider_pos+i+1, 0);
        dlul_print_upload(num, ul);
        if (selected)
           wattroff(dlul_win, A_REVERSE);
    }

    /*** Put filename for hilit task (if any) in statusbar ***/
    if (filename_in_status && fn_at_cursor)
        sl_set(fn_at_cursor);

    sl_draw();
    drw(dlul_win);

    free(heading);
    if (var_field_index >= 0)
        dlul_field_info[var_field_index].len = 0;
}

void dlul_print_download(int index, const download_t *task)
{
    dlul_print_task(index, task->nick, task->fn, task->state, task->size,
                    task->bsz, task->pos, task->p_time);
}

void dlul_print_upload(int index, const upload_t *task)
{
    dlul_print_task(index, task->nick, task->fn, task->state, task->size,
                    task->bsz, task->pos, task->p_time);
}

void dlul_print_task(int index, const char *nick, const char *fn, int state,
                     size_t size, size_t bsz, size_t pos, time_t p_time)
{
    int      percent_done;
    time_t   tm;
    size_t   bytes;
    double   speed;
    int      y, x;
    int      i, spos;

    for (i=0; i < DLUL_NUM_FIELDS; i++) {
        switch (dlul_field_info[i].id) {
        case FLD_FILENAME: default:
            ds_sprintf(&field_buf, "%2d. %-*.*s", index+1,
                       dlul_field_info[i].len - 4, dlul_field_info[i].len - 4,
                       fn);
            break;
        case FLD_USER:
            ds_sprintf(&field_buf, "%-*.*s", dlul_field_info[i].len,
                       dlul_field_info[i].len, nick);
            break;
        case FLD_PROGRESS:
            if (state == IN_PROGRESS) {
                percent_done = (int)(100.0 * (double)pos / (double)size);
                bytes = pos - bsz;
                tm = time(NULL) - p_time;
                speed = tm ? bytes/1024.0/tm : 0.0;
                spos = ds_sprintf(&field_buf, "%d%% (%.2f k/s)", percent_done,
                                  speed);
            }
            else
                ds_set(&field_buf, dlul_get_status_desc(state));
        }
        waddstr(dlul_win, ds_get(&field_buf));
        if (i < DLUL_NUM_FIELDS-1) {
            waddch(dlul_win, ' ');
            wvline(dlul_win, ACS_VLINE, 1);
            getyx(dlul_win, y, x);
            wmove(dlul_win, y, x+1);
            waddch(dlul_win, ' ');
        }
    }
    whline(dlul_win, ' ', COLS - ds_len(&field_buf));
}

const char *dlul_get_status_desc(int status)
{
    switch (status) {
    case QUEUED:
        return "Queued";
    case RQUEUED: case RRQUEUED:
        return "Remotely Queued";
    case REQUESTED:
        return "Requested";
    case WAITING:
        return "Waiting";
    case CONNECTING: case CONNECTING1:
        return "Connecting";
    case FAILED:
        return "Failed";
    case TIMED_OUT:
        return "Timed Out";
    case INCOMPLETE:
        return "Interrupted";
    case COMPLETE:
        return "*Complete*";
    default:
        return "?";
    }
}

int dlul_input(WINDOW *win, sock_t *m)
{
    chtype       c;
    download_t  *dl;
    upload_t    *ul;
    int          screen_pos;
    sock_t      *sk;
    int          purged_down, purged_up;
    int          num_retried, num_ret_queued;
    int          r;

    c = wgetch(dlul_win);
    if (c == '\e') {           /* ESC */
        c = wgetch(dlul_win);
        if (c == ERR)
            return(1);
        else
            c+=128;
    }

    if (sl_handle_keystroke(c)) {
        dlul_refresh();
        return(1);
    }

    switch (c) {

    case KEY_F(1):
    case 128 + '1':
    case 'q':
        switchtoscreen(MAIN_SCREEN);
        break;

    case KEY_F(2):
    case 128 + '2':
    case '\t':
        switchtoscreen(RESULT_SCREEN);
        break;

    case '/':
        ccmd = NULL;
        strcpy(cbuf, "/");
        curr = 0;
        curx = strlen(cbuf);
        switchtoscreen(MAIN_SCREEN);
        break;

    case 'h': case 'H':
        sl_set(dlul_help);
        filename_in_status = FALSE;
        dlul_refresh();
        break;

    case 12:        /* Ctrl-L */
      clearok(dlul_win, 1);
      wrefresh(dlul_win);
      break;

    case KEY_UP:    /* cursor up */
        if (dlul_section == 'u' && dlul_cursor == 0 && num_dl > 0) {
            dlul_section = 'd';
            dlul_cursor = num_dl;
        }
        else
            dlul_cursor--;
        filename_in_status = TRUE;
        dlul_refresh();
        break;

    case KEY_DOWN:  /* cursor down */
      if (dlul_section == 'd' && dlul_cursor == num_dl-1 && num_ul > 0) {
            dlul_section = 'u';
            dlul_cursor = 0;
        }
        else
            dlul_cursor++;
        filename_in_status = TRUE;
        dlul_refresh();
        break;

    case KEY_PPAGE: /* PgUp */
    case 16:        /* Ctrl-P */
        dlul_cursor -= (dlul_section == 'd' ? dl_room : ul_room) - 1;
        filename_in_status = TRUE;
        dlul_refresh();
        break;

    case KEY_NPAGE: /* cursor down by 10 */
    case 14:        /* Ctrl-N */
    case 22:        /* Ctrl-V */
        dlul_cursor += (dlul_section == 'd' ? dl_room : ul_room) - 1;
        filename_in_status = TRUE;
        dlul_refresh();
        break;

    case 128 + KEY_UP:      /* switch sections */
    case 128 + KEY_DOWN:
    case 'o':
        if (dlul_section == 'd') {
            if (!num_ul)
                break;
            dlul_section = 'u';
            /* Try to leave the cursor at around the same screen offset in the
               other section: */
            screen_pos = dlul_cursor - dl_first_disp;
            dlul_cursor = ul_first_disp + screen_pos;
        }
        else {
            if (!num_dl)
                break;
            dlul_section = 'd';
            /* Try to leave the cursor at around the same screen offset in the
               other section: */
            screen_pos = dlul_cursor - ul_first_disp;
            dlul_cursor = dl_first_disp + screen_pos;
        }
        filename_in_status = TRUE;
        dlul_refresh();
        break;

    case 'd':       /* kill task at cursor */
        if (dlul_section == 'd') {
            if (dlul_cursor >= num_dl) {
                sl_set("Nothing to delete");
                break;
            }
            list_nth(dl, down, dlul_cursor);
            if (!dl)
                break;
            wp(wchan, "* Deleted download of \"%s\" from %s\n", dl->fn,
               dl->nick);
            sl_sprintf("Deleted download of \"%s\" from %s", dl->fn,
                       dl->nick);
            ddownload(wchan, dl);
        }
        else {
            if (dlul_cursor >= num_ul) {
                sl_set("Nothing to delete.");
                break;
            }
            list_nth(ul, up, dlul_cursor);
            if (!ul)
                break;
            wp(wchan, "* Deleted upload of \"%s\" to %s\n", ul->fn, ul->nick);
            sl_sprintf("Deleted upload of \"%s\" to %s", ul->fn, ul->nick);
            dupload(ul);
        }
        filename_in_status = FALSE;
        dlul_refresh();
        break;

    case 'f':       /* force download at cursor, if applicable */
        if (dlul_section == 'u') {
            sl_sprintf("Can't force an upload.");
	    filename_in_status = FALSE;
	    dlul_refresh();
            break;
        }
        sk = findsock("server");
	if (!sk) {
	    sl_set("Not connected to server.");
	    filename_in_status = FALSE;
	    dlul_refresh();
	    break;
	}
	list_nth(dl, down, dlul_cursor);
	if (!dl) {
	    sl_sprintf("Nothing to force.");
	} else {
	    r = forcedownload(sk->fd, dl);

	    switch(r) {
	    case 0: 
	      wp(win, "* Getting \"%s\" from %s\n", dl->fn, dl->nick);
	      sl_sprintf("Getting \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    case -1: 
	      sl_sprintf("Already getting \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    default:
	      break;
	    }
        }
        filename_in_status = FALSE;
        dlul_refresh();
        break;

    case 'r':       /* retry download at cursor, if applicable */
    case '\n':
    case '\r':
        if (dlul_section == 'u') {
            sl_sprintf("Can't retry an upload.");
	    filename_in_status = FALSE;
	    dlul_refresh();
            break;
        }
        sk = findsock("server");
	if (!sk) {
	    sl_set("Not connected to server.");
	    filename_in_status = FALSE;
	    dlul_refresh();
	    break;
	}
	list_nth(dl, down, dlul_cursor);
	if (!dl) {
	    sl_sprintf("Nothing to retry.");
	} else if (dl->state == COMPLETE) {
	    sl_sprintf("Download already finished (use 'f' to force) - \"%s\".", dl->fn);
	} else {
	    r = retrydownload(sk->fd, dl);

	    switch(r) {
	    case 0: 
	      wp(win, "* Getting \"%s\" from %s\n", dl->fn, dl->nick);
	      sl_sprintf("Getting \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    case 1: case 2:
	      wp(win, "* Queued download of \"%s\" from %s\n", dl->fn, dl->nick);
	      sl_sprintf("Queued download of \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    case 3:
	      wp(win, "* Remotely queued download of \"%s\" from %s\n", dl->fn, dl->nick);
	      sl_sprintf("Remotely queued download of \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    case -1: 
	      sl_sprintf("Already getting \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    case -2:
	      sl_sprintf("Download already queued (use 'f' to force) - \"%s\" from %s", dl->fn, dl->nick);
	      break;
	    default:
	      break;
	    }
        }
        filename_in_status = FALSE;
        dlul_refresh();
        break;

    case 'R':
        num_retried = 0;
        num_ret_queued = 0;
        sk = findsock("server");
	if (!sk) {
            sl_set("Not connected to server");
	    filename_in_status = FALSE;
	    dlul_refresh();
	    break;
	}
	list_forall(dl, down) {
	  if (ACTIVE(dl->state) || dl->state == QUEUED ||
	      dl->state == RQUEUED || dl->state == COMPLETE) {
	      continue;
	  }
	  r = retrydownload(sk->fd, dl);

	  switch(r) {
	  case 0: 
	    wp(win, "* Getting \"%s\" from %s\n", dl->fn, dl->nick);
	    num_retried++;
	    break;
	  case 1: case 2:
	    wp(win, "* Queued download of \"%s\" from %s\n", dl->fn, dl->nick);
	    num_retried++;
	    num_ret_queued++;
	    break;
	  case 3:
	    wp(win, "* Remotely queued download of \"%s\" from %s\n", dl->fn, dl->nick);
	    num_retried++;
	    num_ret_queued++;
	    break;
	  case -1: case -2: default:
	    break;
	  }
	}

	sl_sprintf("%d downloads retried (%d queued)", num_retried,
		   num_ret_queued);
        filename_in_status = FALSE;
        dlul_refresh();
        break;

    case 'P':       /* purge stopped tasks */
        switch (dlul_section) {
	case 'u':
	  purged_up = purgeup();
	  wp(wchan, "* Purged %d uploads\n", purged_up);
	  sl_sprintf("Purged %d uploads", purged_up);
	  break;
	case 'd': default:
	  purged_down = purgedown(wchan);
	  wp(wchan, "* Purged %d downloads\n", purged_down);
	  sl_sprintf("Purged %d downloads", purged_down);
	  break;
	}
        filename_in_status = FALSE;
        dlul_refresh();
        break;

    default:
        if (c >= '1' && c <= '9') {
            dlul_cursor = (c - '1');
            filename_in_status = TRUE;
            dlul_refresh();
        }
        break;
    }

    return(1);
}
