/************************************************************************/
/* Copyright 2013 MBARI													*/
/************************************************************************/
/* Summary	: Platform-specific routines for porting Embedded ZModem to BEDS*/
/* Filename : bedszm.c													*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: Benthic Event Detection System (BEDS)						*/
/* Revision : 1.0														*/
/* Created	: 10/2/2013													*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/************************************************************************/
/* Modification History:												*/
/* 2oct2013 rah - created												*/
/************************************************************************/
/* The following copyright is included because some routines were taken */
/* from Omen's rbunix.c.  Note that MBARI has licensed Industrial ZModem*/
/* from Omen Technology.												*/
/*
 *	This file contains Linux/Unix specific code for setting terminal modes,
 *	very little is specific to ZMODEM per se 
 *	Copyright 2011 Omen Technology INC All Rights Reserved
 */
/************************************************************************/

#include <cfxpico.h>			/* Persistor PicoDOS definitions		*/
#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <beds.h>				/* BEDS controller definitions			*/
#include <syslog.h>				/* BEDS System logger					*/
#include <file.h>				/* BEDS file I/O definitions			*/
#include <fileUtils.h>			/* BEDS file utilities definitions		*/
#include <modem.h>				/* BEDS acoustic modem software			*/

#undef TRUE
#undef FALSE

#include "izm.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>


/****************************************/
/*		External Data					*/
/****************************************/

Extern MBool	zmSetRemote;	/* Use remote commands to set SRegs for ZModem*/
Extern int		Verbose, errcnt, blklen, Eofseen;
Extern char		*secbuf;
Extern unsigned Txwindow, Tframlen;
Extern char		Lzconv;

#ifdef NOCOPYTX
Extern char privsecbuf[];
#endif


/****************************************/
/*		Module Local Data				*/
/****************************************/

MLocal FILE		*infp = NULL;
MLocal struct stat filestat;


/************************************************************************/
/* Function	   : sendline												*/
/* Purpose	   : Contrary to the name, send one CHARACTER to the serial port*/
/* Inputs	   : Character to send										*/
/* Outputs	   : None													*/
/************************************************************************/
void sendline(int c)
{
	CIOputc((char)(c & 0xff));
}

/************************************************************************/
/* Function	   : readline												*/
/* Purpose	   : Read one CHARCTER from the host						*/
/* Inputs	   : Timeout, in tenths of a second							*/
/* Outputs	   : None													*/
/************************************************************************/
int readline(int timeout)
{
	short tmout, chr;
	int	  remaining;
#define TMOUT_MAX		327
		
	for (remaining = timeout; remaining > 0; remaining -= tmout)
	{
		tmout = (remaining > TMOUT_MAX ? TMOUT_MAX : remaining);
		chr = SCIRxGetCharWithTimeout((short)(100*tmout));
		if (chr != -1)
			return(chr & 0xff);
	}
	return(TIMEOUT);
}

/************************************************************************/
/* Function	   : checkline												*/
/* Purpose	   : Determine if there are characters available			*/
/* Inputs	   : None													*/
/* Outputs	   : Non-zero if chars available, else zero					*/
/************************************************************************/
int checkline(void)
{
	return(CIOgetq());
}

/************************************************************************/
/* Function	   : vfile													*/
/* Purpose	   : Log an error											*/
/* Inputs	   : format string, 4 parameters							*/
/* Outputs	   : None													*/
/************************************************************************/
void vfile(char *fmt, ...)
{
	va_list		ap;
 
	va_start(ap, fmt);
	if (Verbose > 2) {
		vSysLogPrintf(LOG_ZMODEM, fmt, ap);
	}
}

/************************************************************************/
/* Function	   : zperr													*/
/* Purpose	   : Log an error											*/
/* Inputs	   : format string, 2 parameters							*/
/* Outputs	   : None													*/
/************************************************************************/
void zperr(char *s, ...)
{
	va_list		ap;
 
	va_start(ap, s);
	if (Verbose > 0) {
		vSysLogPrintf(LOG_ZMODEM, s, ap);
	}
}

/************************************************************************/
/* Function	   : insertStat												*/
/* Purpose	   : Insert file status information into buffer				*/
/* Inputs	   : char ptr												*/
/* Outputs	   : Length of inserted string								*/
/************************************************************************/
int		insertStat(char *q)
{
	sprintf(q, "%lu %lo", filestat.st_size, filestat.st_mtime);
	return(strlen(q));
}

/************************************************************************/
/* Function	   : zseek													*/
/* Purpose	   : Seek to given position in nput file					*/
/* Inputs	   : File position											*/
/* Outputs	   : Current file offset, or -1 if function failed			*/
/************************************************************************/
int zseek(long n)
{
	clearerr(infp);								/* Clear EOF */
	return(fseek(infp, n, SEEK_SET));
}

/************************************************************************/
/* Function	   : zfilbuf												*/
/* Purpose	   : Fill buffer with blklen chars							*/
/* Inputs	   : None													*/
/* Outputs	   : Number characters read									*/
/************************************************************************/
int		zfilbuf(void)
{
	size_t n;

#ifdef NOCOPYTX
	n = fread(secbuf=privsecbuf, 1, blklen, infp);
#else
	n = fread(secbuf, 1, blklen, infp);
#endif
	if (n < blklen)
		Eofseen = 1;
	return(n);
}

/************************************************************************/
/* Function	   : zclose													*/
/* Purpose	   : Close the input file									*/
/* Inputs	   : status													*/
/* Outputs	   : None													*/
/************************************************************************/
void zclose(int status)
{
#ifdef DIAGS
	vfile("zclose: status=%d", status);
#endif
	if (infp != NULL) {
		fclose(infp);
	} else {
#ifdef DIAGS
		vfile("file already closed!!");
#endif
	}

	infp = NULL;
}

/****************************************************************/
/* Functions to support calling ZModem from BEDS code			*/
/****************************************************************/

/************************************************************************/
/* Function	   : dsZSend												*/
/* Purpose	   : Func sent to dirScan to send file via ZModem			*/
/* Inputs	   : dirent ptr (per dirScan)								*/
/* Outputs	   : TRUE to continue, FALSE to stop						*/
/************************************************************************/
MLocal MBool dsZSend(struct dirent *de, Errno *rtnp)
{
	char *name;

	name = de->d_name;
	if ((de->d_attr & (_A_SUBDIR | _A_VOLID)) == 0)
	{
		if ((infp=fopen(name, "r"))==NULL)
		{
			++errcnt;
			return(TRUE);				/* pass over it, there may be others */
		}
	}
	stat(name, &filestat);				/* Get file size				*/

	*rtnp = wcs(name);					/* Send one file				*/
	sysLogPrintf("%s sent via ZModem, result = %d\n", name, *rtnp);

	return(*rtnp == IZOK ? TRUE : FALSE); /* If failed protocol, stop	*/

} /*dsZSend() */


/************************************************************************/
/* Function	   : zmSendFiles											*/
/* Purpose	   : Send multiple files via ZModem prototcol				*/
/* Inputs	   : Name (wildcards supported), verbosity, window size, frame length*/
/* Outputs	   : OK or ERROR											*/
/* Comment	   : For default, set verbose=0, window=0					*/
/************************************************************************/
int zmSendFiles(char *name, int verbose, unsigned window, unsigned framelen)
{
	Errno		rtn = OK;
	int remModem = -1, remS3, remS8;
	Verbose = verbose;
	Txwindow = window;
	Tframlen = framelen;
	errcnt = 0;

	sysLogPrintf("Starting ZModem send for %s\n", name);

	RTCDelayMicroSeconds(500000);
	if (zmSetRemote && isModemPort() && modemCommandMode())
	{
		if ((remModem = getSReg(14)) >= 0)
		{
			remS3 = getRemoteSReg(remModem, 3);
			remS8 = getRemoteSReg(remModem, 8);

			if ((remS3 >= 0) && (remS3 & 0x80))
				setRemoteSReg(remModem, 3, remS3 & 0x7f);
			if (remS8 > 10)
				setRemoteSReg(remModem, 8, 10);
		}

		modemOnlineMode();
	}

	startz();
	dirScan(name, dsZSend, &rtn);
	saybibi();

	CIOoflush();
	sysLogPrintf("ZModem finished, rtn = %d, errcnt = %d\n", rtn, errcnt);

	if (zmSetRemote && isModemPort() && (remModem >= 0) && 
		((remS3 & 0x80) || (remS8 > 10)))
		if (modemCommandMode())
		{
			if ((remS3 >= 0) && (remS3 & 0x80))
				setRemoteSReg(remModem, 3, remS3);
			if (remS8 > 10)
				setRemoteSReg(remModem, 8, remS8);

			modemOnlineMode();
		}

	return(rtn);
}
