/************************************************************************/
/* 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);
}
