#include "fidoterm.h"
#include "mem.h"
#include <xfbuf.h>
#include <filexfer.h>
#include <ctype.h>
#include <time.h>

long sec2dos();
char *protocolname(int);

/*
	ESCape F)ile ... command.


filexfer()	ESCape F)ile... command prompt. Calls down() from
		ASCIO.C

xfer_status()	ESC F)ile F)inal command. Re-display the last 
		file transfer status window.

autoreceive();	Handle auto-receive ZMODEM 

char *protocolname() Returns a pointer to the name of the 
		current file transfer protocol.

Support routines protocol modules:

xferstat(n,z,s)	Controls error reporting and status logging for
int n;		file transfers. N is the function call number;
long z;
char *s;	0 == Initialize: clears the file status counters
			totl_???? to zero, and readies the display.

		1 == start new file: Displays the passed filename (s)
			and overall file size (z). 

		2 == status display: current block number, etc. Z
			is current byte offset. s is a runtime message.

		3 == error message: similar to above but can be
		handled differently if necessary.

		When z == -1L it means the filesize is not known.

bad_name(s)	Returns true if the passed ASCII name is a device
		name.

baudrate()	Returns the current baud rate.

fexist(fn)	Returns true if this file cannot be overwritten;
char *fn;	ie. if it exists and overwriting is not allowed by
		the program.

chkabort()	Checks the local keyboard and returns true if the
		file transfer should be aborted.

*/

/* These are for the status display. */

static WINDOW box = {1,8,3,74,A_NORMAL,W_DOUBLEBOX,
    "    File:                                           Time:\r"
    "  Status:                                        Elapsed:\r"
    "                                          Data bytes/sec:\r"
    "      At:                               Total data bytes:\r"
    "    Size:                                Port bytes sent:\r"
    "Protocol:                              Port bytes recv'd:\r"
};

/* Define component locations via macros (easier to manually edit) */

#define FILENAME 0,0			/* filename and # position */
#define MSG 1,10			/* xfer status msg */
#define BYTES 3,10			/* current byte position */
#define FILESIZE 4,10			/* total file size */
#define PROTOCOL 5,10			/* protocol name */

#define TIME 0,61			/* current time */
#define ELAPSED 1,60			/* elapsed time */
#define BYTESEC 2,58			/* bytes/sec */
#define TOTALBYTES 3,58			/* total data bytes */
#define SENDPORT 4,58			/* raw receive byte count */
#define RECVPORT 5,58			/* raw receive byte count */

static char last_fn[SS] = "";		/* last filename */
static char last_fmt[SS];		/* xferstat()-passed format string (for checking) */
static char last_msg[SS];		/* last status msg (formatted) */
static time_t start_time,last_time;	/* xfer start & end time */
static long last_byte;			/* last file byte position */
static long last_size;			/* last file size, or -1L if not known */
static int last_filemode;		/* last set filemode */
static long last_in,last_out;		/* chars in and out */
static unsigned last_fileno;		/* files this transfer session */
static long last_total_bytes;		/* total for the transfer session */

static int logfile;			/* text log file handle */
static long rawtime;			/* numeric DOS date */
static char dostime[] = "23:59:59";	/* preformatted for your convenience */


/* Automatically start a ZMODEM file receive. */

autoreceive() {

	filemode= ZMODEM;
	zrecv(zmpath,"",0); 
	xfer_end();
}

/* File upload/download */

filexfer() {
char *cp,c,buff[SS];

	while (1) {
		c= inputc("FILE COMMAND","\004R\005eceive  \004T\005ransmit  \004C\005apture  \004P\005rotocol  \004S\005tatus  \004L\005ist-Files      ");
		if (c == cmdchar) return;
		switch (c) {
			case 't': upload(); return;	/* upload a file */
			case 'r': download(); return;	/* download a file */
			case 'l': dir(); return;	/* list disk files */
			case 's': xfer_status(); return;/* previous file transfer status */
			case 'p': getfmode(); return;	/* set protocol */
			case 'c': capture(); return;	/* capture text */
			case CR: return; 		/* a way out */
		}
	}
}

/* Start or stop text capture. */

static capture() {
char *cp;

	if (savfile == -1) {				/* if not capturing */
		cp= input("FILE","Disk file to capture text");
		if (! *cp) return;

		savfile= open(cp,OPEN_RW);
		if (savfile == -1) savfile= creat(cp,CREAT_RW);
		if (savfile == -1) msg("Can't use file %s?",cp);
		else lseek(savfile,0L,2);
		strcpy(captname,cp);			/* remember filename */

	} else {
		close(savfile);
		savfile= -1;
		msg("Capture file closed");
	}
}

/* Choose a file transfer mode. */

static getfmode() {
char f,c;

	f= 200;
	while (f == 200) {
		c= inputc("PROTOCOL","\004Z\005modem  \004X\005modem  \004T\005elink  \004A\005SCII text       ");
		if (c == cmdchar) return;
		switch (c) {
			case 't': f= TELINKC; break;
			case 'x': f= XMODEMC; break;
			case 'c': f= XMODEM; break;
			case 'z': f= ZMODEM; break;
			case 'a': f= ASCII; break;
			case CR: f= filemode; break;
		}
	}
	filemode= f;
}

/* Upload files. */

static upload() {
char *cp;
char *getfname();

	cp= getfname(1);				/* get file/pathnames */
	if (! *cp) return;

	switch (filemode) {
		case ASCII: down(cp); break;

		case XMODEM: case XMODEMC: 
		case TELINK: case TELINKC: 
			bglockout= 1;			/* no background modem polling! */
			xsend(cp,"",0,filemode); break;

		case ZMODEM: 
			bglockout= 1;			/* no background modem polling! */
			zsend(cp,"",0); break;
	}
	xfer_end();
	bglockout= 0;
}

/* Download files. */

static download() {
char *cp,buff[SS];
char *getfname();

	cp= getfname(0);		/* get filenames */
	if (! *cp) return;

	strip_path(buff,cp);		/* buff == path only */

	switch (filemode) {
		case ASCII:
			msg("Use \004F\005ile \004C\005apture to receive text");
			return;

		case XMODEM: case XMODEMC: 
			bglockout= 1;			/* no background modem polling! */
			xrecv(cp,"",0,filemode); break;
		case TELINK: case TELINKC: 
			bglockout= 1;			/* no background modem polling! */
			xrecv(buff,"",0,filemode); break;
		case ZMODEM: zrecv(buff,"",0); break;
	}
	xfer_end();
	bglockout= 0;
}

/* Get file or directory name(s) for upload or download. */

static char *getfname(send)
int send;
{
char *cp,buff[SS];

	switch (filemode) {
		case TELINK: case TELINKC: 
		case ZMODEM: strcpy(buff,"FILE- OR PATH-NAMES"); break;

		default: strcpy(buff,"FILENAME"); break;
	}
	cp= input(buff,(send ? " From your disk      " : "To your disk      "));
	if (! *cp) return(cp);
	fixdir(cp);				/* fixup dirnames */
	return(cp);
}

/* ESCape F)ILE S)TATUS command: Regenerate the latest file transfer 
status display. */

xfer_status() {

	if (*last_fn) {
		init_window(&box);		/* init the box */
		fill_in_box();			/* filename, etc */
		w_pos(&box,MSG);		/* status message */
		w_printf(&box,"%-30s",last_msg);
		update_box();			/* file stats */
		msg("Previous file transfer status shown -- hit any key to continue");

	} else msg("There is no \"previous file transfer status\" to show --  hit any key to continue");

	cursoroff();				/* no cursor */
	while (!keyhit()) {			/* wait for a key */
		if (unattended()) break;	/* no sense waiting! */
	}
	removewindow(&box);			/* remove box */
	signon();				/* restore commandline */
}

/* File transfer complete -- clean up the screen, etc */

static xfer_end() {
long t;

	log_end();				/* close any log session */
	msg("File transfer complete -- hit any key to continue");
	for (t= millisec + 20000L; millisec < t; ) {
		if (unattended()) break;	/* no sense waiting! */
		if (keyhit()) break;
	}
	removewindow(&box);			/* restore the screen */
	signon();				/* re-arm standard prompt */
}

/* Display a file transfer status message. */

xferstat(f,z,zero,s)
int f;		/* type of status message */
long z;		/* file size (when f == 1) or file position (when f == 2) */
int zero;	/* zero -- inline-message-follows indicator for Fido (StringID) */
char *s;	/* status message */
{
unsigned n;

/* Time is returned as seconds-since-1970. Convert to packed DOS format
for the time display. Used as-is for elapsed time. 'timezone' is a MSC 
global, containing the number of seconds difference GMT --> localtime. */

	time(&last_time);				/* get current time */
	rawtime= sec2dos(last_time - timezone);		/* convert for display */
	n= rawtime & 0xffff;				/* time in lower 16 */
	sprintf(dostime,"%2u:%02u:%02u",n >> 11,(n >> 5) & 0x3f,(n & 0x1f) << 1);
	
	switch (f) {
		case XFER_START: 			/* display initialization */
			init_window(&box);		/* clear status display */
			last_fn[0]= '\0';
			last_msg[0]= '\0';
			last_size= -1L;			/* not known */
			char_in_count= char_out_count= 0L;
			last_fileno= 1;
			last_byte= last_total_bytes= 0L;
			last_filemode= filemode;	/* remember filemode */

			start_time= last_time= time(&start_time); /* start elapsed time ctr */
			fill_in_box();
			update_box();
			msg("File transfer in progress -- use the \004ESC\005 key to interrupt");
			cursoroff();
			log_start();			/* text log */
			break;

		case XFER_FILE:
			last_fileno= totl_files + 1;	/* current file number */
			last_filemode= filemode;	/* (XMODEM/CRC, TELINK changes) */
			last_size= z;			/* remember file size */

			last_fn[0]= '\0';		/* set filename, */
			if (strlen(s) >= 30) {
				s += (30 - sizeof("...") - 1);
				strcpy(last_fn,"...");
			}
			strcat(last_fn,s);		/* add name/partial name */
			stoupper(last_fn);		/* make upper case */
			fill_in_box();			/* fill in status box basics */
			log_filename();			/* text log */
			break;
			
/* totl_bytes is set when a file is completed; during a file transfer it
doesn't change. Therefore for data/error reports we have to add them. At
end of file totl_bytes reflects the actual bytes transferred. */

		case XFER_END:				/* end of session */
		case XFER_EOF:				/* end of file */
			last_total_bytes= totl_bytes;	/* updated value */

		case XFER_ERROR:			/* error message */
		case XFER_STATUS:			/* runtime message */
			goto disp;

		case XFER_DATA:				/* runtime status */
			last_total_bytes= totl_bytes + z; /* total for the session */

disp:;			last_byte= z;			/* new file position */
			if (! same(s,last_fmt)) {	/* if a new message */
				strcpy(last_fmt,s);
				_spr(last_msg,&s);	/* format it */
				last_msg[30]= NUL;	/* truncate to fit */
				w_pos(&box,MSG);	/* save and display */
				w_printf(&box,"%-30s",last_msg);
			}
			last_in= char_in_count;
			last_out= char_out_count;

			update_box();			/* update the box */
			switch (f) {			/* yes, again */
				case XFER_EOF: log_eof(); break;
				case XFER_ERROR: log_data(); break;
			}
			break;
	}
}

/* Fill in the file transfer status box basic info. */

static fill_in_box() {
char buff[20];

	if (last_fileno < 2) strcpy(buff,"File");
	else sprintf(buff,"File #%d",last_fileno);
	w_pos(&box,PROTOCOL); w_printf(&box,"%11s",protocolname(last_filemode));
	w_pos(&box,FILENAME); w_printf(&box,"%8s: %-38s",buff,last_fn);

	w_pos(&box,FILESIZE);
	    if (last_size == -1L) w_printf(&box,"  (unknown)");
	    else w_printf(&box,"%11,lu",last_size);	/* total file size */
}

/* Fill in the file transfer status box with the varying data. */

static update_box() {
long t;
int n;

	w_pos(&box,BYTES);		/* file byte position */
	w_printf(&box,"%11,lu",last_byte);

	w_pos(&box,TOTALBYTES);		/* file byte position */
	w_printf(&box,"%11,lu",last_total_bytes);

	w_pos(&box,SENDPORT);		/* total port bytes sent */
	w_printf(&box,"%11,lu",last_out);

	w_pos(&box,RECVPORT);		/* total port bytes received */
	w_printf(&box,"%11,lu",last_in);

	w_pos(&box,TIME);		/* current time */
	w_string(&box,dostime);

	t= last_time - start_time;	/* elapsed time */
	w_pos(&box,ELAPSED);
	w_printf(&box,"%6,lu:%02lu",t / 60L,t % 60L);

	if (t) {			/* calc bytes/sec */
		w_pos(&box,BYTESEC);
		w_printf(&box,"%11,lu",last_total_bytes / t);
	}
}

/* Generate a file transfer text log entry. */

static log_start() {
int n;

	logfile= -1;				/* flag as not-open */
	if (!xferlog[0]) return;		/* skip if no logfile name */

	logfile= open(xferlog,OPEN_RW);		/* open or create log file */
	if (logfile == -1) logfile= creat(xferlog,CREAT_RW);
	else lseek(logfile,0L,2);		/* append to the end */
	if (logfile == -1) return;		/* oops */

	fprintf(logfile,"\r\n+FidoTerm file transfer started on ");

	n= rawtime >> 16L;			/* DOS date in upper 16 */
	fprintf(logfile,"+%2u %s %2u\r\n",
	    n & 0x1f,_months[(n >> 5) & 0x0f],((n >> 9) & 0x3f) + 80);
}

/* Enter a new filename into the log. */

static log_filename() {
char buff[SS];

	if (logfile == -1) return;

	if (last_size == -1L) strcpy(buff,"(unknown)");
	else sprintf(buff,"%,lu",last_size);

	fprintf(logfile,"+%s File #%03d: %s, %s bytes, %s\r\n",
	    dostime,last_fileno,last_fn,buff,protocolname(last_filemode));
}

/* Log a data block or error. */

static log_data() {

	if (logfile == -1) return;

	fprintf(logfile,"=%s %s at %,lu bytes\r\n",dostime,last_msg,last_byte);
}

/* Log end of file. */

static log_eof() {
long t;
unsigned n;

	if (logfile == -1) return;

	fprintf(logfile,"-%s File complete",dostime);
	t= last_time - start_time;	/* elapsed time */

	fprintf(logfile," (%,lu:%02lu",t / 60L,t % 60L);

	if (t > 0L) fprintf(logfile,", %,lu bytes/sec",last_total_bytes / t);
	fprintf(logfile,")\r\n");
}

/* Log end of session. */

static log_end() {

	if (logfile == -1) return;

	fprintf(logfile,"-%s Transfer complete\r\n",dostime);
	fprintf(logfile,"-Port bytes sent: %,lu  Bytes received: %,lu\r\n\r\n",
	    last_out,last_in);
	close(logfile);
}

/* Return the text name of the file transfer method. */

char *protocolname(mode)
int mode;
{	switch (mode) {
		case XMODEM:	return "Xmodem"; 
		case XMODEMC:	return "Xmodem/CRC"; 
		case TELINK:	return "Telink"; 
		case TELINKC:	return "Telink/CRC"; 
		case MODEM7:	return "Modem Batch"; 
		case MODEM7C:	return "Modem Batch/CRC"; 
		case KERMIT:	return "Kermit"; 
		case DIETIFNA:	return "DietIFNA"; 
		case ZMODEM:	return "Zmodem"; 
		case FSC001X:	return "FSC-0001 Xmodem"; 
		case FSC001T:	return "FSC-0001 Telink"; 
		case ASCII:	return "ASCII";
		default: 	return "??"; 
	}
}

/* Return the current baud rate. */

baudrate() {

	return(rate);
}

/* Always allow overwriting. */

fexist(fn)
char *fn;
{
	return(0);
}

/* Check for illegal filenames. Mostly just device names. */

badname(s)
char *s;
{
int f,i;
char name[SS];
struct _xfbuf xfbuf;

	cpyarg(name,s);			/* clean copy, */
	stolower(name);			/* easy testing */
	xfbuf.s_attrib= 0xff;		/* all types */
	if (! _find(name,0,&xfbuf)) 	/* if it doesnt exist, */
		return(0);		/* no prob */

/* NOTE: This uses the direct INT 21 open() and close(). */

	f= _open(name,0);		/* if we can't open it */
	if (f == -1) return(0);		/* it aint there */

	i= _ioctl(0,f,0,0);		/* see if device */
	_close(f);			/* close handle */
	return(i & 0x80);		/* 0x80 is the 'device' bit */
}

/* Check for keyboard abort; return true if we should abort. */

chkabort() {
char a;

	if (keyhit() == cmdchar) {
		bglockout= 1;			/* lock out backgroudn polling */
		while (keyhit());		/* flush all input */
		a= ask("Abort file transfer");
		if (a) stop_script();
		msg("File transfer in progress -- use the \004ESC\005 key to interrupt");
		cursoroff();
		bglockout= 1;
		return(a);
	}
	return(0);
}

static fprintf(file,f)
int file;
char *f;
{
char buf[100];

	_spr(buf,&f);
	write(file,buf,strlen(buf));
}

