/*      Timed execution routine. Starts a timer and executes a sequence
 *	of commands when expired.
 *
 *	Added by IW0CNB - Feb 1992
 *  'at mm' format added by WG7J - 920805
 *
 *      Added by WA7TAS Oct 1992:
 *
 *         'at k' command
 *
 *         Repeating AT command
 *           - bug fix June 2, 1993
 *           - merged changes into Johan's new code June 14, 1993
 *
 */
#include "global.h"
#include "commands.h"
#ifdef MSDOS
#include <dos.h>
#include "hardware.h"
#else
#include <time.h>
#include "timer.h"
#include "proc.h"
#endif

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: at.c,v 1.18 1997/09/07 21:18:28 root Exp $";
#endif


static void atcmd (char *command);
static void atproc (int i, void *p1, void *p2);
extern struct timer *Timers;
extern struct cmds Cmds[];


/* List of events; We keep note of all timer processes generated by the
 * at command.
 */
struct at_list {
	struct at_list *next;	/* Linked-list pointer */
	struct timer *at_timer;
	char recur[10];		/* used in recursive 'at' commands*/
	unsigned int id;	/* numerical 'id' of this 'at' */
};

#define	NULLATLIST	(struct at_list *)0


static struct at_list *Head_loe = NULLATLIST;	/* Head of List Of Events */
static int id = 1;		/* next 'at' assigned gets this number */


int
doat (int argc, char **argv, void *p OPTIONAL)
{
struct date *exp_date;
struct time *exp_time;
struct timer *t;
char *cp, *dp;
unsigned int tid;
int i, notf;
time_t nowtime;
unsigned long firsttime;
struct tm tm;
struct at_list *loe, *pp = (struct at_list *)0;	/* List of events */
const char *Errmsg = "Usage:\nat yymmddhhmm <cmd>\nat hhmm <cmd>\nat mm <cmd>\nat now+hhmm <cmd>\nat k <id num> <id num> ...\n";

	if (argc < 2) {		/* Print list of pending at commands */
		tputs ("List of events:\n");

		for (t = Timers; t != NULLTIMER; t = t->next) {
			if (t->func == (void (*)(void *)) atcmd) {
				loe = Head_loe;
				while (loe != NULLATLIST) {
					if (loe->at_timer == t)
						break;
					loe = loe->next;
				}
				(void) time (&nowtime);
				nowtime = (time_t) ((time_t)read_timer (t) / (time_t)1000L + nowtime);
				cp = ctime (&nowtime);
				rip (cp);
				dp = strchr (t->arg, '|');
				if (dp)
					*dp = 0;
				tprintf ("At: %s - ID: %5u - Command: %s\n", cp, (loe) ? loe->id : 0, (char *) t->arg);
				if (dp)
					*dp = '|';
			}
		}
		return 0;
	}
	if (argc < 3) {
		tputs (Errmsg);
		return 0;
	}
	if (argv[1][0] == 'k') {
		i = 2;
		while (i < argc) {
			tid = (unsigned) atoi (argv[i]);
			if (strlen (argv[i]) > 5 || tid == 0 || atol (argv[i]) > 65535) {
				tputs ("Invalid ID #.\n");
				return 0;
			}
			loe = Head_loe;
			notf = 1;
			while (loe != NULLATLIST) {
				if (loe->id == tid) {
					stop_timer (loe->at_timer);
					free (loe->at_timer->arg);
					free (loe->at_timer);
					if (loe == Head_loe) {
						Head_loe = loe->next;
					} else {
						if (pp)
							pp->next = loe->next;
					}
					free (loe);
					tprintf ("at id: %u--Killed.\n", tid);
					notf = 0;
					break;
				}
				pp = loe;
				loe = loe->next;
			}
			if (notf)
				tprintf ("  ID %u not found.\n", tid);
			i++;
		}
		return 0;
	}
	exp_date = (struct date *) mallocw (sizeof (struct date));
	exp_time = (struct time *) mallocw (sizeof (struct time));

	cp = mallocw (5);

	switch (strlen (argv[1])) {
		case 10:	/* Full date and time given */
			cp[0] = argv[1][0];
			cp[1] = argv[1][1];
			cp[2] = '\0';

			exp_date->da_year = 1900 + atoi (cp);
			if (exp_date->da_year < 1970)
				exp_date->da_year += 100;
			/*		if(exp_date->da_year > 1999) goto error;	*/

			cp[0] = argv[1][2];
			cp[1] = argv[1][3];
			cp[2] = '\0';

			exp_date->da_mon = (char) atoi (cp);
			if (exp_date->da_mon > 12)
				goto error;

			cp[0] = argv[1][4];
			cp[1] = argv[1][5];
			cp[2] = '\0';

			exp_date->da_day = (char) atoi (cp);
			if (exp_date->da_day > 31)
				goto error;

			cp[0] = argv[1][6];
			cp[1] = argv[1][7];
			cp[2] = '\0';

			exp_time->ti_hour = (char) atoi (cp);
			if (exp_time->ti_hour > 23)
				goto error;

			cp[0] = argv[1][8];
			cp[1] = argv[1][9];
			cp[2] = '\0';

			exp_time->ti_min = (char) atoi (cp);
			if (exp_time->ti_min > 59)
				goto error;

			exp_time->ti_sec = 0;
			exp_time->ti_hund = 0;

			(void) time (&nowtime);
			firsttime = (unsigned long) dostounix (exp_date, exp_time);
			if (firsttime < (unsigned long) nowtime)
				goto error;

			break;

		case 4:	/* Only time given, so apply current date */
			tnos_getdate (exp_date);
			cp[0] = argv[1][0];
			cp[1] = argv[1][1];
			cp[2] = '\0';

			exp_time->ti_hour = (char) atoi (cp);
			if (exp_time->ti_hour > 23)
				goto error;

			cp[0] = argv[1][2];
			cp[1] = argv[1][3];
			cp[2] = '\0';

			exp_time->ti_min = (char) atoi (cp);
			if (exp_time->ti_min > 59)
				goto error;

			exp_time->ti_sec = 0;
			exp_time->ti_hund = 0;

			(void) time (&nowtime);
			firsttime = (unsigned long) dostounix (exp_date, exp_time);
			if (firsttime <= (unsigned long) nowtime) {	/* Requested time has passed */
				firsttime += 86400L;	/* So book him for tomorrow */
			}
			break;


		case 2:	/* Only minutes given, so apply current time & date - WG7J */
			tm.tm_min = (char) atoi (argv[1]);
			if (tm.tm_min > 59)
				goto error;

			/* get today's date */
			tnos_getdate (exp_date);
			tm.tm_year = exp_date->da_year - 1900;
			tm.tm_mday = exp_date->da_day;
			tm.tm_mon = exp_date->da_mon - 1;

			/* get current time */
			gettime (exp_time);
			tm.tm_hour = exp_time->ti_hour;
			/* if we're already past the minute, do it next hour ! */
			if (exp_time->ti_min > tm.tm_min)
				tm.tm_hour++;

			/* now adjust this for day boundaries, etc. */
			tm.tm_sec = 0;
			tm.tm_isdst = 0;
			firsttime = (unsigned) mktime (&tm);
			(void) time (&nowtime);
			break;

		case 8:	/* now+hhmm given */
			strncpy (cp, argv[1], 4);
			cp[4] = '\0';
			if (strcmp (cp, "now+") != 0)
				goto error;

			cp[0] = argv[1][4];
			cp[1] = argv[1][5];
			cp[2] = '\0';

			firsttime = (unsigned long) atoi (cp) * 3600L;

			cp[0] = argv[1][6];
			cp[1] = argv[1][7];
			cp[2] = '\0';

			firsttime += (unsigned long) atoi (cp) * 60L;
			(void) time (&nowtime);
			firsttime += (unsigned long) nowtime;
			break;

		default:
		      error:tputs (Errmsg);
			free (exp_date);
			free (exp_time);
			free (cp);
			return 0;

	}			/* switch */

	free (cp);
	free (exp_time);
	free (exp_date);

	t = (struct timer *) mallocw (sizeof (struct timer));

	set_timer (t, (int32) (firsttime - (unsigned long) nowtime) * 1000L);
	t->state = TIMER_RUN;
	t->func = (void (*)(void *)) atcmd;
	t->arg = (char *) mallocw (strlen (argv[2]) + 12);	/*crh*/
	sprintf (t->arg, "%s|%d", argv[2], id);

	/* Add the new timer to the head of List Of Events */
	loe = (struct at_list *) mallocw (sizeof (struct at_list));

	/*
	 * if timer is recursive, set at_list->recur and tack
	 *  the '+' character to the end of the timer argument
	 */
	loe->recur[0] = 0;
	if (argv[2][strlen (argv[2]) - 1] == '+') {
		strncpy (loe->recur, argv[1], 10);
		strcat (t->arg, "+");
	}
	loe->at_timer = t;
	loe->id = (unsigned) id++;
	/*
	 * start the timer
	 */
	start_detached_timer (t);
	/*
	 * an id of 0 is invalid
	 */
	if (id == 0)
		id = 1;
	loe->next = Head_loe;
	Head_loe = loe;

	return 0;
}


/* Process that actually handles 'at' execution */
void
atproc (int i OPTIONAL, void *p1, void *p2 OPTIONAL)
{
char *command;
struct at_list *loe, *p;
int recur, theid;
char *pp;
char cmd[80];

	command = (char *) p1;
	if (!command)
		return;

	log (-1, "AT command: %s", command);
	/* check for recursion */
	if (command[strlen (command) - 1] == '+')
		recur = 1;
	else
		recur = 0;
	/* locate id in command string */
	pp = strchr (command, '|');
	if (pp)	{
		*pp++ = 0;
		theid = atoi (pp);
	} else
		theid = 0;

	/* Free up memory for expired at commands */
	p = Head_loe;
	for (loe = Head_loe; loe != NULLATLIST; loe = loe->next) {
		if (loe->at_timer->state == TIMER_EXPIRE) {
			if (loe->id != (unsigned) theid)
				continue;	/* is this the proper entry? */
			if (recur) {
				sprintf (cmd, "at %s \"%s\"", loe->recur, command);
				command[strlen (command) - 1] = 0;
			}
			free (loe->at_timer);	/* Free timer */
			if (loe == Head_loe) {
				Head_loe = loe->next;
				p = loe->next;
				free (loe);
				loe = p;
				p = Head_loe;
			} else {
				if (p)	{
					p->next = loe->next;
					free (loe);
					loe = p->next;
				}
			}
			break;	/* exit for loop */
		} else {	/* Not expired, go on */
			if (loe != Head_loe)
				p = p->next;
		}
	}
	(void) cmdparse (Cmds, command, NULL);	/* Go with requested command */

	if (recur)
		(void) cmdparse (Cmds, cmd, NULL);

	free (command);
}


/* Function to be called on timer expiration to execute a command */
static void
atcmd (command)
char *command;
{
	(void) newproc ("AT handler", 1024, atproc, 0, (void *) command, NULL, 0);
}
