#include <ascii.h>
#include "fido.h"
#include <xfbuf.h>
#include "fidomem.h"
#include "proto.h"

static void net_start();

/* Execute a FidoNet event. For normal FidoNet events, this ends any
CONTINUOUS event, starts the specified event and runs it to completion. For
CONTINUOUS events, it starts it if not already started, and runs it until
it's time is up, then ends it. This returns true if any kind of action
was performed in net_run() such that we need to redisplay messages or
init the modem when it gets back to main(). */

int do_fidonet(e)
unsigned e;
{
char tag;
FLAG action;

	tag= fido.sched[e].tag;			/* (its easier to type) */
	if (! net_set()) return(1);		/* select areas/basic defaults */
	open_node();				/* open basic nodelist files */
	if (node_files()) {			/* if problem with nodelist */
		clprintf(SM+149);		/* say "bad nodelist" */
		net_end();			/* close any event */
		close_node();			/* close nodelist files */
		return(1);			/* exit now */
	}
	action= 0;				/* nothing happened yet */

/* If an event is running (ie. CONT) then open the correct nodemap. If the 
event 'e' is different than the one running last (ie. we're changing events) 
close it first. */

	open_nmap(fido.event);			/* use correct nodemap */
	if (e != fido.event) {			/* if new/different event */
		net_end();		 	/* stop old event */
		++action;			/* screen action occurred */
	}

/* Run IDLE events now. Since it will not be the same event number, the 
previous event, if any, was just closed out. */

	if (tag == '!') {			/* if IDLE event */
		close_node();			/* dont need nodelist files */
		idleevt(e);			/* run IDLE */
		return(1);
	}

/* If not the same event as last time through here, make new packets before
running the event. (Phase 1.) */

	if (e != fido.event) {			/* if a new/different event, */
		net_start(e);			/*   create packets */
		++action;			/*   we wrote to the screen */
	}

/* Packets are made, nodemaps complete, etc. Load event-specific modem stuff
and run Phase 2 FidoNet. */

	if (net_run()) ++action;		/* run til completion, */
	new_mail();				/* decompose incoming packets */
	if (! doscode) markevt(fido.event);	/* mark complete if no error/abort */
	if (!cont(e)) clrcevts();		/* if normal evt, clear CONTs */
	close_node();				/* else just close node files */
	return(action);
}

/* Continuous mail function; receive an incoming packet now and
decompose it. */

void inmail(sync_char)
int sync_char;		/* sync character that got us here */
{
int i;
struct _xfbuf xfbuf;
struct _node *d;
char *cp,fn[SS],buff[SS];

	gtod(buff);
	cprintf(SM+150,buff);				/* "incoming mail" */
	lprintf(LM+34,buff);

	if (! net_set()) return;
	open_node();					/* open nodelist */
	if (fido.event) open_nmap(fido.event);		/* open correct nodemap */
	recv_mail(sync_char);				/* process mail, */
	new_mail();					/* unpack it all */
	gtod(buff);
	cprintf(SM+152,&buff[11]);			/* time of completion */
	lprintf(LM+35,&buff[11]);
	close_node();
	close_up();					/* close everything else */
}

/* If there is a CONT FidoNet event running, go update packets as necessary. */

void net_new() {

	if (fido.event == -1) return;		/* no event running */
	if (!cont(fido.event)) return;		/* not a CONT event */

	open_log("fidonet.log");		/* open log file, */
	net_set();				/* select areas, etc */
	open_node();				/* open node list, */
	open_nmap(fido.event);			/* specific nodemap */
	re_packet();				/* generate packets */
	close_node();
	close_log();
}

/* End any FidoNet event running, kill packets, etc. Usually when about to run
another event or when Fido is manually stopped. */

static end_fidonet() {

	if (fido.event == -1) return;

	open_log("fidonet.log");	/* dont forget to log it! */
	open_node();			/* open nodelist files */
	open_nmap(fido.event);		/* and correct nodemap */
	if (node_files()) return;	/* not open! */
	net_set();
	net_end();			/* close it up */
	close_node();
	fido_settings();		/* reload FIDO.INI stuff */
}

/* Start up a FidoNet event; build packets for unscheduled pickup or
RUSH outgoing calls. */

static void net_start(e)
int e;
{
int i;
char tag,*cp,buff[SS];
struct _xfbuf xfbuf;

	tag= fido.sched[e].tag;			/* its easier to type ... */
	dsp_event(buff,e);			/* display it, */
	cprintf(SM+134);			/* " * " */
	cputs(buff);				/* the event info */

	gtod(buff);
	lprintf(LM+36,tag,buff,FIDOVER,96+FILEVER);
	if (taskid) clprintf(SM+22,taskid);	/* "TaskID #" */

	ntc_file();				/* create new NODELIST.NTC */
	open_nmap(e);				/* open/create correct nodemap */
	if (node_files()) {			/* if problem with nodelist */
		clprintf(SM+149);		/* say "bad nodelist" */
		fido.event= -1;
		return;
	}
	open_route(tag,e);			/* fill in nodemap with route info */

	pktnum= 0;				/* first incoming packet name */
	i= xfbuf.s_attrib= 0;
	makefname(buff,"*.IN");			/* don't step on any existing */
	while (_find(buff,i++,&xfbuf)) {	/* .IN files */
		if (atoi(xfbuf.name) > pktnum) pktnum= atoi(xfbuf.name);
	}
	if (evtabort()) return;			/* manual abort */

	fido.event= e;				/* mark which one is running */
	fido.tag= tag;				/* and its tag */
	putsys();				/* write now (in case shared) */

/* Count the messages to be sent, and assemble the packets. If none, then
we are to receive mail only. The abort is used if there is a full disk; if
so, set the flag to prevent any mail at all, just wait out the time. */

	set_abort(0);
	if (was_abort()) {
		clprintf(SM+154);
		clprintf(SM+155);
		killstuff();			/* kill *.OUT, *.FLO, *.REQ */

	} else make_packets();			/* create packets, etc */
}

/* Set basic FidoNet setup and load the right message and file areas. Returns 0
if there is an error. */

static net_set() {
char *cp;
int i;

	abort= 0;				/* no Control-C abort */
	limit= 0;				/* stop time limit, */
	hlimit= 0;
	lmtstate= 255;				/* disable it */
	for (cp= (char *)&caller, i= sizeof(struct _clr); i--;)
		*cp++= NUL;			/* zap the caller record */
	caller.keys= KALL;			/* all the keys */
	caller.stuff= 0xfff80L;			/* SYSOP, 24 x 80, etc */

	if (!getmarea(fido.netmarea)) {
		clprintf(SM+156);		/* not fidonet msg area */
		return(0);
	}
	if (! getfarea(fido.netfarea)) {
		clprintf(SM+157);		/* ditto file area */
		return(0);
	}
	*langpath= NUL;
	msgcount();				/* count messages */

/* Override settings meant for callers. */

	filearea.locks= msgarea.locks= LNONE; 		/* no locks */
	filearea.priv= msgarea.priv= TWIT; 		/* no priv */
	msgarea.flags= filearea.flags= (AREA_OVW | AREA_UPLD); /* allow overwrite */
	return(1);
}

/* Execute the FidoNet event setup previously. Return false if we do nothing. */

static net_run() {
int recd;
int action,interval,c,i,n;
char tag,*cp,buff[SS];
FLAG rdymsg;			/* 1 == we did the ready message */
FLAG had_mail;			/* 1 == we had outgoing mail this schedule */
FLAG more_mail;			/* 1 == still mail to send/calls to make */
FLAG run_mail;			/* 1 == event not over yet */

	if (fido.event == -1) return(0);

	tag= fido.sched[fido.event].tag;	/* its easier to type ... */
	sched_settings(tag);			/* load schedule-specific settings */
	rdymsg= 0;				/* we want a message */
	clr_clk();				/* no elapsed time */
	interval= 0;				/* call immediately */

/* We need to know how many packets there are, to calculate a "random" index
to choose the next node to call with. Also if this is a CONTINUOUS event,
we have to clear the DIAL-TRIES for each record. */

	for (i= 0; get_ntc(i) != -1; ) {
		if (cont(fido.event)) {
			if (! (ntc.bits & NTC_SUCCESS)) {
				ntc.tries= 0;	/* clear dial-tries */
				put_ntc(i);	/* for each session */
			}
		}
		++i;				/* count another, */
	}
	had_mail= more_mail= next_node(i);	/* seed first pass */
	run_mail= 1;				/* run unless told to stop */
	action= 0;				/* nothing happened yet */
	if (cont(fido.event)) send_only= 1;	/* means same thing here */

	while (1) {
		c= bdos(6,0xff);
		switch (tolower(c)) {		/* poll the keyboard */
			case ETX:		/* Control-C */
				run_mail= 0;
				doscode= 1; 
				break;

			case '3': case 'e':	/* list events */
				til_sched('?',-1,1); /* find an event right NOW */
				rdymsg= 0;
				break;

			case '4': case 'p':	/* list packets */
				mail_pending();
				rdymsg= 0;
				break;

			case '5': case 'c':	/* immediate call */
				interval= 0; 
				rdymsg= 0;
				break;

			case '6': case DLE:	/* early end */
				run_mail= 0;
				cprintf(SM+162);
				break;

			case '7': case 'i':	/* init the modem */
				discon();	/* disconnect/clear first */
				if (modem_init(0)) rdymsg= 0;
				break;

			case '?':
			case '/':
				gtod(buff);
				cprintf(0,"%s\377",buff);
				cprintf(SM+159);
				cprintf(SM+160);
				cprintf(SM+161);
				rdymsg= 0;
				break;
		}
		if (doscode) break;		/* someone aborted */
		if (! run_mail) break;		/* early ended */

		limit= 0;			/* no time limit here */

/* If the modem is not idle, and this is a CONT event, return immediately;
we let Fido handle all incoming calls. */

		i= answer();			/* check the modem, */
		if (i && cont(fido.event)) 	/* if modem active in CONT event */
			return(action);		/* return to Fido */

		if (i > 0) { 			/* if an incoming call, */
			recv_mail(0);		/* get mail, */
			delay(500);		/* let telco settle, */
			interval= 0;		/* dial immediately after */
			rdymsg= 0;		/* need a message, */
			clprintf(FM+1);
			action= 1;		/* well, we answered the phone */
			continue;

		} else if (i == -2) rdymsg= 0;	/* just went idle */

/* If there are packets waiting to be sent, and the random delay is up,
and the modem is idle, attempt to send one. If complete, mark the 
one just sent. */

		if (send_only) interval= 0;		/* if send-only, send now! */
		if (!recv_only && !i && (minutes >= interval)) {
			more_mail= 0;			/* assume no more mail */
			recd= have_node();		/* get next node to call */
			if (recd != -1) {		/* if found one previously */
				modem_init(17);		/* init the modem if it's not */
				send_mail(recd);	/* send it, */
				interval= dial_interval;/* dial every minute */
				clr_clk();
				more_mail= next_node(0);/* find next node to call */
				rdymsg= 0;		/* which checks NEXT call */
				clprintf(FM+1);
				action= 1;		/* well, we dialed */
			}
		}

/* Check for reasons to stop running FidoNet. (Checking til_event() now 
allows 0-length events to run one pass.) */

		if (til_event(fido.event)) break;	/* event over */
		if (doscode != 0) break;		/* error/-errorlevel */
		if ((rush(fido.event) || cont(fido.event)) && !more_mail) break; /* stop if Rush & no mail */

		if (modem_init(17)) rdymsg= 0;		/* init the modem every 17 min */

/* Finally nothing immediate to do; put up the prompt. */

		if (! rdymsg) cprintf(SM+164);
		rdymsg= 1;
	}

/* Event is over -- if run_mail was cleared (Control-C, Control-P) or not
a CONT event, end the event, otherwise leave this as an active but pending
event. */

	if (!cont(fido.event)) run_mail= 0;		/* end evt if not CONT */
	if (! run_mail) {				/* see comment */
		net_end();				/* end normal events */
		clrcevts();				/* clear CONT events */
		++action;
	}
	return(action);
}

/* Mail time over. Delete our outgoing packets, start processing any 
packets we received. */

static net_end() {
char tag,buff[SS];

	if (fido.event == -1) return;

	tag= fido.sched[fido.event].tag;

	gtod(buff);
	lprintf(SM+165,&buff[11]);
	end_sched();				/* process outgoing */

	gtod(buff);
	cprintf(SM+166,tag,&buff[11]);
	lprintf(LM+37,tag,&buff[11]);

	if (! doscode) markevt(fido.event);	/* mark if no error/abort */
	fido.event= -1;
	putsys();				/* (in case shared) */
}

/* Unpack any packets. This uses the force abort stuff to handle
disk full. Fido may be run in mail mode at anytime, then type Control-C;
all packets remaining will be unpacked. */

static new_mail() {
int i;
struct _xfbuf xfbuf;
char path[SS],file[SS];
struct _node d;

	msgcount();					/* count msgs in msg area */

	set_abort(0);
	if (was_abort()) {				/* how to recover ... */
		clprintf(SM+167);
		clprintf(SM+168);
		clprintf(SM+169);
		clprintf(SM+170);

	} else {
		xfbuf.s_attrib= 0;
		i= 0; makefname(path,"*.IN");		/* packets in upload path */
		while (_find(path,i++,&xfbuf)) {
			if (! isdigit(*xfbuf.name)) continue;
			makefname(file,xfbuf.name);
			unpacket(file);
		}

		cpy_node(&d,&id);			/* default to "us" */
		i= 0; makefname(path,"*.FLI");		/* do .FLIs without packets */
		while (_find(path,i++,&xfbuf)) {
			if (i == 1) clprintf(SM+171);
			if (! isdigit(*xfbuf.name)) continue;
			makefname(file,xfbuf.name);
			unfile(file,&d);
		}
	}
}

/* Do the end of schedule cleanup and reporting. */

static end_sched() {
int i,n;
char buff[SS];
struct _ntc tally;		/* end of schedule tally */
int packets,success,pickups;	/* more tally stuff */
FLAG prevpkt;			/* previous packet info lost */

	killstuff();				/* kill *.OUT, *.FLO, *.REQ */

	prevpkt= 0;
	packets= pickups= success= 0;
	tally.tries= tally.totl_tries= tally.connects= 0; /* clear the tally */
	tally.files= tally.msgs= 0;
	tally.time= tally.xfer_time= 0;
	tally.bytes= 0L;

	for (n= i= 0; get_ntc(i) != -1; i++) {	/* scan for ANY success */
		if (ntc.bits & NTC_SUCCESS) {
			n= 1;			/* found one */
			break;
		}
	}
	if (n) for (i= 0; get_ntc(i) != -1; i++) { /* only if we had one success */

		if (packets == 0) {
			clprintf(SM+172);
			clprintf(SM+173);	/* table header */
			clputs("---------------+------+------+------+------+------+------+------+------+------\377");
		}
		++packets;
		tally.tries += ntc.tries;
		tally.totl_tries += ntc.totl_tries;
		tally.connects += ntc.connects;
		tally.time += ntc.time;
		tally.xfer_time += ntc.xfer_time;
		tally.bytes += ntc.bytes;
		tally.msgs += ntc.msgs;
		tally.files += ntc.files;

		strcpy(buff,str_node(&ntc.node));
		if (ntc.bits & NTC_KILLED) {
			prevpkt= 1;
			strcat(buff,"*");
		}
		clprintf(0,"%14s | %4u | %4u | %4u | %4u | ",buff,ntc.totl_tries,ntc.connects,ntc.msgs,ntc.files);
		clprintf(0,"%02u:%02u| ",ntc.time / 60,ntc.time % 60);

		clprintf(0,"%4u | %4u | %4u | ",
		    nearestn(ntc.bytes,1024L),		/* total K bytes */
		    lidiv(ntc.bytes,ntc.time),		/* connect BPS */
		    lidiv(ntc.bytes,ntc.xfer_time));	/* peak BPS */

		if (ntc.bits & NTC_PD) {
			++pickups;
			clprintf(0,"PickUp");

		} else if (ntc.bits & NTC_SUCCESS) {
			++success;
			clprintf(0,"YES");

		} else clprintf(0,"NO");

		clprintf(FM+1);
	}

	if (prevpkt) clprintf(SM+175);	/* "previous packet to this node was killed" */

	if (packets) {
		clprintf(SM+176);
		clprintf(SM+177);
		clprintf(0,"---------------+-------+-------+-------+-------+-------+-------+---------\r\n");
		clprintf(0,"%14d | %5u | %5u | %5u | %5u | %5u | ",
		    packets,tally.totl_tries,tally.connects,tally.msgs,tally.files,(int)((tally.bytes + 1023L) / 1024L));

		clprintf(0,"%02u:%02u  | %d\r\n",tally.time / 60,tally.time % 60,pickups);

		clprintf(SM+178,(long) (success * 10000L) / (long) (packets * 100L));

		clprintf(SM+179,(tally.connects > 0) ? ((long) (success * 10000L) / (long) (tally.connects * 100L)) : 0);

	} else clprintf(SM+180); /* "no packets sent or picked up" */

/* Clear all IN-TRANSIT and ORPHAN bits in all messages. */

	clearmsg();

/* This must be done LAST, since it may change the paths set above. */

	if (success) markusr();
}

/* Main for an IDLE event. Just waits for the sched to end. */

static idleevt(event)
int event;
{
FLAG f;
char buff[SS];

	gtod(buff);
	clprintf(SM+181,buff);

	f= 1;
	while (f) {
		switch (bdos(6,0xff)) {		/* keyboard poll */
			case '?':
			case '/':
				gtod(buff);
				cprintf(0,"%s\377",buff);
				cprintf(SM+183);
				cprintf(FM+1);
				break;

			case DLE:
				f= 0;		/* set magic flag */
				break;

			case ETX: 		/* abort */
				doscode= 1; break;
		}
		if (doscode) break;
		if (til_event(event)) break;	/* ... while this event is current */
	}

	if (! doscode) markevt(event);		/* mark as complete */
}

/* Check the keyboard for Control-C, set doscode and return true. */

int evtabort() {
	if (bdos(6,0xff) == ETX) {		/* if Control-C */
		cprintf(SM+184);
		doscode= 1;
	}
	return(doscode);			/* return it (or previous) */
}

/* Kill the outgoing temporary files. */

static killstuff() {
char buff[SS];

	makeuname(buff,"*.OUT");
	killall(buff);			/* delete output packets */
	makeuname(buff,"*.FLO");
	killall(buff);			/* delete output file lists */
	makeuname(buff,"*.REQ");
	killall(buff);			/* delete output file lists */
}

/* Delete all matching files in the given path. */

static killall(s)
char *s;
{
int i;
char path[SS],file[SS];
struct _xfbuf xfbuf;

	i= 0;
	xfbuf.s_attrib= 0;
	strip_path(path,s);			/* save a copy of the path, */
	while (_find(s,i++,&xfbuf)) {		/* find each file */
		strcpy(file,path);		/* build the full filename */
		strcat(file,xfbuf.name);
		delete(file);			/* kill it */
	}
}
