/************************************************************************/
/* Copyright 2013-2018 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												*/
/* 13mar2018 rah - ported to BEDS2 using FatFs							*/
/************************************************************************/
/* 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 <mbariTypes.h>			/* MBARI type definitions				*/
#include <beds.h>				/* BEDS controller definitions			*/
#include <syslog.h>				/* BEDS System logger					*/
#include <fatFs/ff.h>			/* FatFs definitions					*/
#include <file.h>				/* BEDS file I/O definitions			*/
#include <fileUtils.h>			/* BEDS file utilities definitions		*/
#include <clock.h>				/* BEDS Clock module					*/
#include <modem.h>				/* BEDS acoustic modem software			*/
#include <utils.h>				/* BEDS Utility routines				*/

#undef TRUE
#undef FALSE

#include "izm.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>


/****************************************/
/*		External Data					*/
/****************************************/

Extern Int32	modemPort;				/* Serial port for  acoustic modem	*/
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 FIL		infp;
MLocal FILINFO	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)
{
	_mon_putc((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 = ser_getc_tmout(getConsole(), (short)(tmout * (TICKS_PER_SEC/10)));
		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(kbhit());
}


/************************************************************************/
/* Function	   : purgeline												*/
/* Purpose	   : Flush input											*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void purgeline(void)
{
	serRxFlush(getConsole());
}


/************************************************************************/
/* Function	   : flushmo												*/
/* Purpose	   : Wait for Tx complete									*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void flushmo(void)
{
	serWaitTxComplete(getConsole());
}


/************************************************************************/
/* 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, "%u %o", filestat.fsize, filestat.ftime);
	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)
{
	infp.err = 0;					/* clearerr(&infp);				*/
	return((f_lseek(&infp, n) == FR_OK) ? 0 : -1);
}

/************************************************************************/
/* Function	   : zfilbuf												*/
/* Purpose	   : Fill buffer with blklen chars							*/
/* Inputs	   : None													*/
/* Outputs	   : Number characters read									*/
/************************************************************************/
int		zfilbuf(void)
{
	UINT	n;

#ifdef NOCOPYTX
	if (f_read(&infp, secbuf=privsecbuf, blklen, &n) != FR_OK)
		n = 0;
#else
	if (f_read(&infp, secbuf, blklen, &n) != FR_OK)
		n = 0;
#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
	f_close(&infp);
}

/****************************************************************/
/* 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(FILINFO *de, void *p)
{
	char	*name;
	Errno	*rtnp = (Errno *)p;

	name = de->fname;
	if ((de->fattrib & (AM_DIR | AM_VOL)) == 0)
	{
		if (f_open(&infp, name, FA_READ) != FR_OK)
		{
			++errcnt;
			return(TRUE);				/* pass over it, there may be others */
		}
	}
	f_stat(name, &filestat);			/* Get file size				*/

	*rtnp = wcs(name);					/* Send one file				*/
	sysLogPrintf("%s sent via ZModem, result = %d\r\n", name, *rtnp);

	return(*rtnp == OK ? 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;
	MBool		isModemPort;

	Verbose = verbose;
	Txwindow = window;
	Tframlen = framelen;
	errcnt = 0;
	isModemPort = (getConsole() == modemPort);

	sysLogPrintf("Starting ZModem send for %s", name);

	clkDelayUs(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();

	serTxFlush(getConsole());
	serRxFlush(getConsole());
	sysLogPrintf("ZModem finished, rtn = %d, errcnt = %d", 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);
}
