/***********************************************
 *  event.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"

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

extern CommandDef com[];

extern DCharBuf *args;

extern ListDef *active_list;

extern char *current, *last_stream;

extern flag_t ignore_hist_input;

/************************************
 *        Local variables           *
 ************************************/
static char *EVENTYPES[] = { 
    "TMD (timed)",
    "EOL (end of list)",
    "EOQ (end of queue)",
    "EOS (end of stream)",
    "SOS (start of stream)",
    "EXT (exit)"
};
static char *EVENTIDS[] = { 
    "TMD", 
    "EOL", 
    "EOQ", 
    "EOS", 
    "S0S", 
    "EXT" 
};
static char *YesNo[] = { 
    "No", 
    "Yes" 
};
static char tmpstr_ev[COMM_SIZE];
static unsigned int event_mask = 0;
static int event_ids = 0;
static EventData *ev_start = NULL, *ev_end = NULL;

/************************************
 *         Module routines          *
 ************************************/

unsigned int g_event_bytec()
{
    EventData *curr = ev_start;
    unsigned int bytec = 0;

    while (curr) {
	bytec += sizeof(EventData);
	bytec += get_strlen(curr->command);
	bytec += get_strlen(curr->info);
	curr = curr->next;
    }

    return bytec;
}

/************************************
 *         Event Manipulation       *
 ************************************/

void post_event(unsigned int type)
{
    event_mask |= type;
}

void clear_events()
{
    EventData *curr = NULL;

    while (ev_start) {
	curr = ev_start->next;
	free(ev_start->command);
	free(ev_start->info);
	free(ev_start);
	ev_start = curr;
    }

    event_ids = 0;
}

void add_event(flag_t persist, flag_t quiet, unsigned int type,
	       time_t period_s, char *command, char *info)
{
    if (ev_start) {
	ev_end->next = malloc(sizeof(EventData));
	ev_end = ev_end->next;
    } else {
	ev_start = malloc(sizeof(EventData));
	ev_end = ev_start;
    }

    if (type == TIME_EVENT) {
	ev_end->start = time(NULL);
	ev_end->period = period_s;
    } else {
	ev_end->start = 0;
	ev_end->period = 0;
    }

    if (info)
	ev_end->info = strdup(info);
    else
	ev_end->info = NULL;

    ev_end->id = event_ids++;
    ev_end->type = type;
    ev_end->persist = persist;
    ev_end->quiet = quiet;
    ev_end->command = strdup(command);
    ev_end->next = NULL;
}

int remove_event(int event_id)
{
    EventData *curr, *prev;

    curr = ev_start;
    prev = NULL;

    while (curr) {
	if (curr->id == event_id)
	    break;
	else {
	    prev = curr;
	    curr = curr->next;
	}
    }

    if (curr) {
	if (curr == ev_end)
	    ev_end = prev;

	if (prev)
	    prev->next = curr->next;
	else
	    ev_start = curr->next;

	free(curr->command);
	free(curr->info);
	free(curr);

	if (ev_start == NULL)
	    event_ids = 0;

	return TRUE;
    }

    return FALSE;
}

char *get_evstring(unsigned int type)
{
    switch (type) {
    case TIME_EVENT:
	return EVENTYPES[0];
    case LIST_EVENT:
	return EVENTYPES[1];
    case QUEUE_EVENT:
	return EVENTYPES[2];
    case ESTREAM_EVENT:
	return EVENTYPES[3];
    case SSTREAM_EVENT:
	return EVENTYPES[4];
    case EXIT_EVENT:
	return EVENTYPES[5];
    default:
	return "Unknown event";
    }
}

char *get_evid(unsigned int type)
{
    switch (type) {
    case TIME_EVENT:
	return EVENTIDS[0];
    case LIST_EVENT:
	return EVENTIDS[1];
    case QUEUE_EVENT:
	return EVENTIDS[2];
    case ESTREAM_EVENT:
	return EVENTIDS[3];
    case SSTREAM_EVENT:
	return EVENTIDS[4];
    case EXIT_EVENT:
	return EVENTIDS[5];
    default:
	return "Unknown";
    }
}

void run_event(EventData * ev)
{
    flag_t quiet, persist;

    quiet = ev->quiet;
    persist = ev->persist;

    if (persist)
	ev->start = time(NULL);

    safe_strcpy(tmpstr_ev, ev->command, COMM_SIZE);

    if (!quiet) {
	if (have_tty()) {
	    print_ctrl_str(1, TC_CR);
	    print_ctrl_str(1, TC_CLR_TOEOL);
	    outflush();
	}
	show_user(STATUS_MSG " Executing %s event [%d]:",
		  get_evid(ev->type), ev->id);
    }

    ignore_hist_input = TRUE;
    handle_user_message(tmpstr_ev);
    ignore_hist_input = FALSE;

    if (!quiet)
	show_user(STATUS_MSG " Event [%d] done.", ev->id);

    if (!persist)
	remove_event(ev->id);

    if (!quiet && have_tty())
	update_prompt(FALSE);
}

void check_list_event(EventData * curr)
{
    if (!curr->info)
	return;

    if ((strcmp(curr->info, "*") == 0 ||
	 (active_list && strcmp(curr->info, active_list->dcb->name) == 0)))
	run_event(curr);
}

void check_stream_event(EventData * curr, char *str)
{
    int len, len2;

    if (!curr->info)
	return;

    if (strcmp(curr->info, "*") == 0)
	run_event(curr);
    else {
	len = get_strlen(curr->info);
	len2 = get_strlen(str);
	if (len && len2 && len2 > len &&
	    strcasecmp(&str[len2 - len], curr->info) == 0)
	    run_event(curr);
    }
}

void check_events()
{
    EventData *temp, *curr;
    unsigned int mask;

    curr = ev_start;
    temp = NULL;
    mask = event_mask;
    event_mask = 0;

    while (curr) {
	temp = curr->next;

	switch (curr->type) {
	case TIME_EVENT:
	    if ((curr->start + curr->period) <= time(NULL))
		run_event(curr);
	    break;
	case LIST_EVENT:
	    if (mask & LIST_EVENT)
		check_list_event(curr);
	    break;
	case QUEUE_EVENT:
	    if (mask & QUEUE_EVENT)
		run_event(curr);
	    break;
	case ESTREAM_EVENT:
	    if (mask & ESTREAM_EVENT)
		check_stream_event(curr, last_stream);
	    break;
	case SSTREAM_EVENT:
	    if (mask & SSTREAM_EVENT)
		check_stream_event(curr, current);
	    break;
	case EXIT_EVENT:
	    if (mask & EXIT_EVENT)
		run_event(curr);
	    break;
	}

	curr = temp;
    }
}

void print_add_event_help()
{
    print_cmnd_help(ADD_EVENT);

    show_user(STATUS_MSG " Use '-p' to make event persistent (repeating)");
    show_user(STATUS_MSG " Use '-q' to disable event alerts (quiet)");
    show_user(STATUS_MSG
	      " For <type_options...>, use one of the following:");
    show_user("\t1) -t <seconds>           : TMD (timed) event");
    show_user("\t2) -l ('*' | <list_name>) : EOL (end of list) event");
    show_user("\t3) -q                     : EOQ (end of queue) event");
    show_user("\t4) -s ('*' | <filetype>)  : EOS (end of stream) event");
    show_user("\t5) -r ('*' | <filetype>)  : SOS (start of stream) event");
    show_user("\t6) -e                     : EXT (exit) event");
    show_user(STATUS_MSG
	      " For <command>, enter command(s) as you would by themselves");
}

void /* user */ add_event_comm()
{
    char *opt, *time, *info;
    int cm_ndx;
    time_t time_s;
    unsigned int type;
    flag_t persist, quiet;

    cm_ndx = 1;
    time_s = 0;
    opt = g_dbuf(args, cm_ndx++);
    time = info = NULL;
    type = TIME_EVENT;
    persist = quiet = FALSE;

    if (!opt) {
	print_add_event_help();
	return;
    }

    while (opt) {
	if (strcmp(opt, "-p") == 0)
	    persist = TRUE;
	else if (strcmp(opt, "-q") == 0)
	    quiet = TRUE;
	else if (strcmp(opt, "-e") == 0) {
	    type = EXIT_EVENT;
	    break;
	} else if (strcmp(opt, "-t") == 0) {
	    type = TIME_EVENT;
	    time = g_dbuf(args, cm_ndx++);
	    if (time && is_all_digits(time))
		time_s = (time_t) atoi(time);
	    else {
		print_add_event_help();
		return;
	    }
	    break;
	} else if (strcmp(opt, "-l") == 0) {
	    type = LIST_EVENT;
	    info = g_dbuf(args, cm_ndx++);

	    if (!info) {
		print_add_event_help();
		return;
	    }

	    break;
	} else if (strcmp(opt, "-q") == 0) {
	    type = QUEUE_EVENT;
	    break;
	} else if (strcmp(opt, "-s") == 0) {
	    type = ESTREAM_EVENT;
	    info = g_dbuf(args, cm_ndx++);

	    if (!info) {
		print_add_event_help();
		return;
	    }

	    break;
	} else if (strcmp(opt, "-r") == 0) {
	    type = SSTREAM_EVENT;
	    info = g_dbuf(args, cm_ndx++);

	    if (!info) {
		print_add_event_help();
		return;
	    }

	    break;
	} else {
	    print_add_event_help();
	    return;
	}
	opt = g_dbuf(args, cm_ndx++);
    }

    *tmpstr_ev = ZCHAR;
    opt = g_dbuf(args, cm_ndx++);
    while (opt) {
	strcat(tmpstr_ev, opt);
	opt = g_dbuf(args, cm_ndx++);
	if (opt)
	    strcat(tmpstr_ev, " ");
    }

    if (*tmpstr_ev == ZCHAR)
	print_add_event_help();
    else
	add_event(persist, quiet, type, time_s, tmpstr_ev, info);
}

void /* user */ remove_event_comm()
{
    char *id_str;
    int id;

    id_str = g_dbuf(args, 1);

    if (!id_str)
	print_cmnd_help(REMOVE_EVENT);
    else if (!ev_start)
	show_user(STATUS_MSG " Event list is empty");
    else if (is_all_digits(id_str)) {
	id = atoi(id_str);
	if (!remove_event(id))
	    show_user(DEBUG_MSG " Event ID %d not found", id);
    } else if (strcmp(id_str, "*") == 0) {
	clear_events();
	show_user(STATUS_MSG " Event list cleared");
    } else
	print_cmnd_help(REMOVE_EVENT);
}

void /* user */ print_events()
{
    EventData *curr;
    time_t now, next;
    char *time_str, tmpt[128];

    curr = ev_start;
    next = 0;
    now = time(NULL);
    time_str = ctime(&now);

    if (!curr) {
	show_user(DEBUG_MSG " Event list is empty");
	return;
    }

    remove_newline(time_str);
    add_outbuffer(STATUS_MSG " Current time is %s", time_str);

    while (curr) {
	add_outbuffer(MISC_MSG " Event ID [%d]", curr->id);
	add_outbuffer("\tType       : %s", get_evstring(curr->type));

	if (curr->info)
	    switch (curr->type) {
	    case LIST_EVENT:
		if (strcmp(curr->info, "*") == 0)
		    add_outbuffer("\tList       : (any)");
		else {
		    add_outbuffer("\tList       : \"%s\"", curr->info);
		    if (!g_list(curr->info))
			add_outbuffer
			    ("\tWARNING    : \"%s\" does not exist",
			     curr->info);
		}
		break;
	    case ESTREAM_EVENT:
	    case SSTREAM_EVENT:
		if (strcmp(curr->info, "*") == 0)
		    add_outbuffer("\tFiletype   : (any)");
		else
		    add_outbuffer("\tFiletype   : %s", curr->info);
		break;
	    }

	add_outbuffer("\tCommand    : %s", curr->command);
	add_outbuffer("\tPersistent : %s", YesNo[curr->persist]);
	add_outbuffer("\tQuiet      : %s", YesNo[curr->quiet]);

	if (curr->type == TIME_EVENT) {
	    next = curr->start + curr->period;
	    if (curr->persist) {
		build_time_string(curr->period, tmpt);
		add_outbuffer("\tPeriod     : %s", tmpt);
	    }
	    build_time_string(next - now, tmpt);
	    add_outbuffer("\tNext       : %s", tmpt);
	} else if (curr->start > 0) {
	    build_time_string(now - curr->start, tmpt);
	    add_outbuffer("\tLast       : %s ago", tmpt);
	}
	curr = curr->next;
    }

    flush_outbuffer();
}
