/************************************************************************/
/* 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						*/
// 9/30/2014 LJCOLETTI - modified for use with MSC (MBARI SENSOR CONTROLLER)
/************************************************************************/
/* 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	*/

#undef TRUE
#undef FALSE

#include "izm.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#include <time.h>


/****************************************/
/*	External Data			*/
/****************************************/

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		*/
/****************************************/

#define OK 		0
#define ERROR 	-1

#define NumberOf(arr)		((sizeof(arr) / sizeof(arr[0])))

static FILE	*infp = NULL;
static struct stat filestat;
static const char *logNames[] = {"SYS.LOG", "MPU.LOG", "SZ.LOG"};

typedef enum {LOG_MSC=0, LOG_MPU, LOG_ZMODEM} SysLogType;

int 		dirScan(char *pathStr, char *matchStr, int (*func)(char *path, struct dirent *, void *), void *parm);
long int 	vSysLogPrintf(int ltype, const char *fmt, va_list vlist);
long 		sysLogPrintf(const char *fmt, ...);

/************************************************************************/
/* Function    : sendline						*/
/* Purpose     : Contrary to the name, send one CHARACTER to the serial port*/
/* Inputs      : Character to send					*/
/* Outputs     : None							*/
/************************************************************************/
void sendline(int c)
{
	SCITxPutChar((ushort)(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(SCIRxQueuedCount());
}

void purgeline(void)
{
	SCIRxFlush();
}

void flushmo(void)
{
	SCITxWaitCompletion();
}

/************************************************************************/
/* 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			*/
/************************************************************************/
static int dsZSend(char *path, struct dirent *de)
{
    int	 rtn;
    char name[128];
	
    snprintf(name, sizeof(name), "%s\\%s", path, 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, see insertStat()	*/

    rtn = wcs(name);		/* Send one file		*/
    
	sysLogPrintf("%s sent via ZModem, result = %d\n", name, rtn);

    return(rtn == IZOK);	/* 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 *path, char *name, int verbose, unsigned window, unsigned framelen)
{
    Verbose = verbose;
    Txwindow = window;
    Tframlen = framelen;
	Lzconv = ZCRESUM;         /* Ask receiver for crash recovery */
    errcnt = 0;

    sysLogPrintf("Starting ZModem send for %s %s\n", path, name);

    RTCDelayMicroSeconds(500000);

    startz();
    dirScan(path, name, dsZSend, NULL);
    saybibi();

	SCITxFlush();
    sysLogPrintf("ZModem finished, errcnt = %d\n", errcnt);
    
    return(errcnt == 0 ? OK : ERROR);
}

/************************************************************************/
/* Function    : dirScan						*/
/* Purpose     : Execute function for every directory entry matching pattern*/
/* Inputs      : Wildcard pattern to match, function ptr, function parm	*/
/* Outputs     : None							*/
/************************************************************************/
int dirScan(char *pathStr, char *matchStr, int (*func)(char *path, struct dirent *, void *), void *parm)
{
    struct dirent	de;
    int		rtn = TRUE;
	
	if (pathStr == NULL)
		pathStr = "C:";
	
    if (matchStr == NULL)
		matchStr = "*.LOG";

    if (DIRFindFirst(pathStr, &de) != dsdEndOfDir)
    {
	do
	{
	    if (DIRMatchName((char *) de.d_name, matchStr))
		if ((rtn = (*func)(pathStr, &de, parm)) == FALSE)
		    break;
	} while (DIRFindNext(&de) != dsdEndOfDir);
    }

    DIRFindEnd(&de);
	
    return(rtn);

} /* dirScan() */

/************************************************************************/
/* Function    : vSysLogPrintf						*/
/* Purpose     : Write an entry into the system log file, vprintf style	*/
/* Inputs      : Format, va_list					*/
/* Outputs     : Number of chars printed				*/
/************************************************************************/
long vSysLogPrintf(int ltype, const char *fmt, va_list vlist)
{
	FILE	*logfp;
    time_t	now;
	long	rtn;
    struct tm	*tmp;

	if ((ltype >= NumberOf(logNames)) ||
	((logfp = fopen(logNames[ltype], "a+")) == NULL))
	return(0);

    now = RTCGetTime( NULL, NULL ); 
    tmp = gmtime(&now);
	
    rtn = fprintf(logfp, "%04d/%02d/%02d %02d:%02d:%02d, ",
		  tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
		  tmp->tm_hour, tmp->tm_min, tmp->tm_sec);

    rtn += vfprintf(logfp, fmt, vlist);
    if (fmt[strlen(fmt)-1] != '\n')
	rtn += fprintf(logfp, "\r\n");
    fclose(logfp);
	
    return(rtn);

} /* vSysLogPrintf() */

/************************************************************************/
/* Function    : sysLogPrintf						*/
/* Purpose     : Write an entry into the system log file, printf style	*/
/* Inputs      : String to write, arguments				*/
/* Outputs     : Number of chars printed				*/
/************************************************************************/
long sysLogPrintf(const char *fmt, ...)
{
    va_list	ap;
 
    va_start(ap, fmt);
    return(vSysLogPrintf(LOG_MSC, fmt, ap));

} /* sysLogPrintf() */


