/***********************************************
 *  listmanip.c                                 
 *                                            
 *  Copyright 2001 Eric Kratzer.              
 *  Includes Xaudio software Copyright (C)
 *           1996-2000 Xaudio Corporation
 *           All Rights Reserved.          
 *
 *  This file is part of imp3sh.
 *                                            
 *  imp3sh is free software; you can distribute
 *  it and/or modify it under the terms of the
 *  GNU General Public License as published by
 *  the Free Software Foundation; either version
 *  2 of the License, or (at your option) any
 *  later version.
 *
 *  imp3sh 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 recieved a copy of the GNU
 *  General Public License along with imp3sh; if
 *  not, write to the Free Software Foundation,
 *  Inc., 59 Temple Place, Suite 330, Boston MA
 *  02111-1307 USA
 ************************************************/

/************************************
 *          Include Files           *
 ************************************/

#include "imp3sh.h"

#define LIST_COMM_SIZE (COMM_SIZE*2)

/************************************
 *       External References        *
 ************************************/

extern DCharBuf *args;

extern ListDef *history, *active_list, *edit_list;

extern char *PLAYMODES[], *SEARCHPRF[];

extern flag_t sort_mode;

/************************************
 *        Local variables           *
 ************************************/

static char tmpstr_pl[LIST_COMM_SIZE], tmpstr2_pl[LIST_COMM_SIZE];

/************************************
 *         Playlist manip           *
 ************************************/

void clear_list(ListDef * lst)
{
    c_dbuf(lst->dcb);
    clear_played(lst);
}

int compare_list(const void *vel1, const void *vel2)
{
    get_sort_id(((DCBNode *) vel1)->str, tmpstr_pl);
    get_sort_id(((DCBNode *) vel2)->str, tmpstr2_pl);

    return strcmp(tmpstr_pl, tmpstr2_pl);
}

void /* user */ sort_list()
{
    if (!edit_list)
	show_user(DEBUG_MSG " Edit list undefined");
    else if (edit_list->dcb->len <= 1)
	show_user(DEBUG_MSG " \"%s\" list size too small",
		  edit_list->dcb->name);
    else {
	sort_mode = edit_list->listview;
	qsort((void *) edit_list->dcb->buf, edit_list->dcb->len,
	      sizeof(DCBNode), compare_list);

	show_user(STATUS_MSG " Sorted %d entries in \"%s\" list",
		  edit_list->dcb->len, edit_list->dcb->name);
    }
}

void /* user */ move_list_index()
{
    char *tok1, *tok2;
    int targ, dest;

    tok1 = g_dbuf(args, 1);
    tok2 = g_dbuf(args, 2);

    if (!(tok1 && tok2 && is_all_digits(tok1) && is_all_digits(tok2)))
	print_cmnd_help(MOVE_LIST_INDEX);
    else if (!edit_list)
	show_user(DEBUG_MSG " Edit list undefined");
    else {
	targ = atoi(tok1);
	dest = atoi(tok2);

	if (s_dbuf(edit_list->dcb, targ, dest))
	    show_user(STATUS_MSG " Swapped \"%s\" list indices %d and %d",
		      edit_list->dcb->name, targ, dest);
	else
	    show_user(ERROR_MSG " Invalid index specified");
    }
}

void print_list(ListDef * lst, int pos, int verbose)
{
    char *pat, *loc, hedr[HEAD_SIZE];
    PlayerDef *plyr;
    DCharBuf *dcb;
    DCBNode *buf;
    int i, len, lpos;

    pat = g_dbuf(args, pos);
    loc = NULL;
    plyr = NULL;
    dcb = lst->dcb;
    buf = lst->dcb->buf;
    lpos = lst->dcb->pos;
    len = lst->dcb->len;

    if (!len) {
	show_user(DEBUG_MSG " \"%s\" list is empty", lst->dcb->name);
	return;
    }

    for (i = 0; i < len; i++) {
	get_stream_id(lst, i, tmpstr_pl);

	if (pat && grep_against_args(pos, tmpstr_pl) == FALSE)
	    continue;

	if (i == lpos)
	    strcpy(hedr, PNTR_MSG);
	else
	    strcpy(hedr, LIST_MSG);

	add_outbuffer("%s %d> %s", hedr, i, tmpstr_pl);

	if (verbose) {
	    loc = get_stream(dcb, i, STR_FILE);
	    plyr = check_format(loc);
	    add_outbuffer("%s\tWeight   : %d", hedr, buf[i].info);
	    if (plyr)
		add_outbuffer("%s\tPlayer   : %s", hedr, plyr->name);
	    else
		add_outbuffer("%s\t  No player defined for this file type!",
			      hedr);
	    add_outbuffer("%s\tLocation : %s", hedr, loc);
	}
    }

    flush_outbuffer();
}

void /* user */ print_list_comm()
{
    char *opt;
    int ver, pos;

    opt = g_dbuf(args, 1);
    ver = FALSE;
    pos = 1;

    if (opt && strcmp(opt, "-l") == 0) {
	ver = TRUE;
	pos++;
    }

    if (edit_list)
	print_list(edit_list, pos, ver);
    else
	show_user(DEBUG_MSG " Edit list undefined");
}

void /* user */ play_list_index()
{
    char *str_index;
    int index;

    str_index = g_dbuf(args, 1);

    if (!(str_index && is_all_digits(str_index)))
	print_cmnd_help(PLAY_LIST_INDEX);
    else if (!edit_list)
	show_user(DEBUG_MSG " Edit list undefined");
    else {
	index = atoi(str_index);

	if (index >= 0 && index < edit_list->dcb->len) {
	    edit_list->dcb->pos = index;
	    play_lindex(edit_list, TRUE);
	} else
	    show_user(DEBUG_MSG " index %d out of range", index);
    }
}

void delete_list_grep(ListDef * lst, char *pat)
{
    int i, rem;

    rem = 0;
    add_outbuffer(STATUS_MSG " \"%s\" list entry(s) removed:", lst->dcb->name);

    for (i = 0; i < lst->dcb->len; i++) {
	get_stream_id(lst, i, tmpstr_pl);
	if (grep_against_args(1, tmpstr_pl)) {
	    add_outbuffer("\t" DEL_ENTRY_MSG " %s", tmpstr_pl);
	    r_dbuf(lst->dcb, i);
	    i--;
	    rem++;
	}
    }

    add_outbuffer(STATUS_MSG" %d entry(s) removed from \"%s\" [%d remain]",
		  rem, lst->dcb->name, lst->dcb->len);
}

void delete_list_range(ListDef * lst, char *str)
{
    int i, low, high;

    get_digit_range(str, &low, &high, 0, lst->dcb->len - 1);

    if (low < high &&
	(low >= 0 && low < lst->dcb->len) &&
	(high >= 0 && high < lst->dcb->len)) {

	add_outbuffer(STATUS_MSG
		      " \"%s\" list entry(s) removed in range %d-%d:",
		      lst->dcb->name, low, high);

	for (i = low; i <= high; i++) {
	    get_stream_id(lst, low, tmpstr_pl);
	    add_outbuffer("\t" DEL_ENTRY_MSG " %s", tmpstr_pl);
	    r_dbuf(lst->dcb, low);
	}

	add_outbuffer(STATUS_MSG
		      " %d entry(s) removed from \"%s\" [%d remain]",
		      (high - low) + 1, lst->dcb->name, lst->dcb->len);
    } else
	add_outbuffer(DEBUG_MSG " Bad index range");
}

void delete_list_index(ListDef * lst, char *str)
{
    int index;

    if (!lst->dcb->len)
	add_outbuffer(DEBUG_MSG " \"%s\" list is empty", lst->dcb->name);
    else if (is_digit_range(str))
	delete_list_range(lst, str);
    else if (is_all_digits(str)) {
	index = atoi(str);

	if (index >= 0 && index < lst->dcb->len) {
	    r_dbuf(lst->dcb, index);
	    add_outbuffer(STATUS_MSG
			  " Removed index %d from \"%s\" list [%d remain]",
			  index, lst->dcb->name, lst->dcb->len);
	} else
	    add_outbuffer(ERROR_MSG " index %d out of range", index);

    } else if (strcmp("*", str) == 0) {
	clear_list(lst);
	add_outbuffer(STATUS_MSG " \"%s\" list cleared", lst->dcb->name);
    } else
	delete_list_grep(lst, str);

    flush_outbuffer();
}

void /* user */ delete_list_index_comm()
{
    char *str;

    str = g_dbuf(args, 1);

    if (!str)
	print_cmnd_help(DELETE_LIST_INDEX);
    else if (!edit_list)
	show_user(DEBUG_MSG " Edit list undefined");
    else {
	delete_list_index(edit_list, str);
	update_list_env();
    }
}

void add_list_grep(ListDef * to, ListDef * from)
{
    int i, len, add;

    add = 0;
    len = from->dcb->len;

    for (i = 0; i < len; i++) {
	get_stream_id(from, i, tmpstr_pl);
	if (grep_against_args(2, tmpstr_pl)) {
	    a_dbuf(to->dcb, g_dbuf(from->dcb, i));
	    add++;
	}
    }

    show_user(STATUS_MSG " %d entry(s) added to \"%s\" list",
	      add, to->dcb->name);
}

void add_list_range(char *str, ListDef * to, ListDef * from)
{
    int i, add, low, high;

    add = 0;
    get_digit_range(str, &low, &high, 0, from->dcb->len - 1);

    if (low < high &&
	(low >= 0 && low < from->dcb->len) &&
	(high >= 0 && high < from->dcb->len)) {
	show_user(STATUS_MSG
		  " Copying \"%s\" entry(s) in range \"%d-%d\" to \"%s\"",
		  from->dcb->name, low, high, to->dcb->name);

	for (i = low; i <= high; i++) {
	    a_dbuf(to->dcb, g_dbuf(from->dcb, i));
	    add++;
	}

	show_user(STATUS_MSG " Copied %d entry(s) to \"%s\" list",
		  add, to->dcb->name);
    } else
	show_user(DEBUG_MSG " Bad index range");
}

void /* user */ add_to_list()
{
    ListDef *from;
    char *s, *namefrom, *smem, *str;
    int i, flen, tlen, index;

    namefrom = g_dbuf(args, 1);
    s = g_dbuf(args, 2);
    smem = NULL;

    if (!namefrom || !s)
	print_cmnd_help(ADD_TO_LIST);
    else {
	from = g_list(namefrom);
	str = tokenize(s, &smem, COMMA);

	if (!edit_list) {
	    show_user(DEBUG_MSG " Edit list undefined");
	    return;
	}

	if (!from) {
	    show_user(DEBUG_MSG " \"%s\" list not found", namefrom);
	    return;
	}

	if (from->dcb->len == 0) {
	    show_user(DEBUG_MSG " \"%s\" list is empty", from->dcb->name);
	    return;
	}

	while (str && *str != ZCHAR) {
	    if (is_digit_range(str))
		add_list_range(str, edit_list, from);
	    else if (is_all_digits(str)) {
		index = atoi(str);

		if (index >= 0 && index < from->dcb->len) {
		    a_dbuf(edit_list->dcb, g_dbuf(from->dcb, index));
		    show_user(STATUS_MSG
			      " Copied index %d from \"%s\" to \"%s\"",
			      index, from->dcb->name,
			      edit_list->dcb->name);
		} else
		    show_user(ERROR_MSG " index %d out of range", index);
	    } else if (strcmp("*", str) == 0) {
		flen = from->dcb->len;
		tlen = edit_list->dcb->len;

		for (i = 0; i < flen; i++)
		    a_dbuf(edit_list->dcb, g_dbuf(from->dcb, i));

		show_user(STATUS_MSG " Copied %d entry(s) to \"%s\" list",
			  edit_list->dcb->len - tlen,
			  edit_list->dcb->name);
	    } else
		add_list_grep(edit_list, from);
	    str = tokenize(NULL, &smem, COMMA);
	}

	update_list_env();
    }
}

void /* user */ print_list_status()
{
    char *name, hedr[HEAD_SIZE];
    ListDef *lst;
    DCharBuf *dcb;
    int i, cnt, skp, len, wght, plsts;
    float prc, prc2;

    name = g_dbuf(args, 1);
    lst = g_list_base();
    len = get_strlen(name);
    plsts = 0;

    if (!len) {
	if (history->dcb->len) {
	    add_outbuffer(MISC_MSG " %s", history->dcb->name);
	    add_outbuffer("\tEntry(s)  : %d/[%d]", history->dcb->len,
			  history->dcb->size);
	    add_outbuffer("\tPosition  : %d <%s>", history->dcb->pos,
			  file_from_path(get_stream(history->dcb,
						    history->dcb->pos,
						    STR_FILE)));
	} else
	    add_outbuffer(DEBUG_MSG " Queue is empty");
    }

    if (!lst)
	show_user(DEBUG_MSG " No lists defined");
    else {
	while (lst) {
	    dcb = lst->dcb;

	    if (len && strcmp(dcb->name, name)) {
		lst = lst->next;
		continue;
	    }

	    plsts++;

	    if (active_list == lst)
		strcpy(hedr, PNTR_MSG);
	    else
		strcpy(hedr, MISC_MSG);

	    if (edit_list == lst)
		add_outbuffer("%s *%s", hedr, dcb->name);
	    else
		add_outbuffer("%s %s", hedr, dcb->name);

	    add_outbuffer("\tPlay mode : %s", PLAYMODES[lst->playmode]);
	    add_outbuffer("\tList view : %s", SEARCHPRF[lst->listview]);

	    if (lst->dcb->len) {
		add_outbuffer("\tEntry(s)  : %d", dcb->len);
		add_outbuffer("\tPosition  : %d <%s>", dcb->pos,
			      file_from_path(get_stream
					     (dcb, dcb->pos, STR_FILE)));

		cnt = skp = 0;
		prc = prc2 = 0.0;

		if (lst->playmode == LEARN_MODE) {
		    wght = get_list_weight(lst);

		    for (i = 0; i < dcb->len; i++)
			if (dcb->buf[i].info >= wght)
			    cnt++;

		    if (dcb->len) {
			prc = ((float) cnt / (float) dcb->len) * 100.0;
			prc2 = 100.0 - prc;
		    }

		    add_outbuffer("\tStatus    : %d%% liked, %d%% dis-liked",
				  (int) prc, (int) prc2);
		    add_outbuffer("\tWeights   : play.%d skip.%d global.%d",
				  lst->play_weight, lst->skip_weight, wght);
		} else {
		    for (i = 0; i < dcb->len; i++)
			if (dcb->buf[i].info > 0)
			    cnt++;
			else if (dcb->buf[i].info < 0)
			    skp++;

		    if (dcb->len) {
			prc = ((float) cnt / (float) dcb->len) * 100.0;
			prc2 = ((float) skp / (float) dcb->len) * 100.0;
		    }

		    add_outbuffer("\tStatus    : %d%% played, %d%% skipped",
				  (int) prc, (int) prc2);
		}
	    } else
		add_outbuffer("\tList is empty");

	    lst = lst->next;
	}

	if (!plsts && len)
	    show_user(DEBUG_MSG " \"%s\" list not found", name);
    }

    flush_outbuffer();
}
