/****************************************************************************/
/* Summary	: BEDS command to decode binary files							*/
/* Filename : decode.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: BEDS (Benthic Event Detection System							*/
/* Revision : 1.1															*/
/* Created	: 11/10/2011													*/
/****************************************************************************/
/* Modification History:													*/
/* 10nov2011 rah - created													*/
/* 11jan2017 rah - Changed Sys Rcd output									*/
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <mbariConst.h>			/* MBARI constants						*/
#ifdef CFX						/* For embedded version					*/
#include <cfxpico.h>			// Persistor PicoDOS Definitions
#include <bedsCmd.h>			/* BEDS Command line processor			*/
#include <utils.h>				/* BEDS Utility routines				*/
#include <adc.h>				/* BEDS Analog Module					*/
#else
#include <decodeMain.h>			/* Standalone or Linux version			*/
#include <math.h>
#endif
#include <beds.h>				/* BEDS general definitions				*/
#include <decode.h>				/* Definitions to decode BEDS files		*/
#include <file.h>				/* BEDS file I/O definitions			*/
#ifdef CFX						/* For embedded version					*/
#include <fileUtils.h>			/* For dirScan()						*/
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <dirent.h>

#ifndef CFX
Flt32	adcEngValue(Nat16 chan, Nat16 counts, char **unitp);
#endif

/********************************/
/*		External Data			*/
/********************************/
#ifdef CFX						/* For embedded version					*/
Extern char		*fiveArgs;
#endif


/************************************************/
/*				Global Data						*/
/* (Because standalone decode app needs them)	*/
/************************************************/

Global MBool	printDataRecs = FALSE;
Global MBool	totalsOnly = FALSE;
Global MBool	pressOnly = FALSE;
Global MBool	sysRecOnly = FALSE;
Global double	epsilon = 0.02;


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

MLocal Nat32	numDataRecs = 0, totDataRecs = 0, deletedDataRecs = 0, duration;

MLocal Void (*rcdFunc[])(FILE *) =
{
	printFileHdr, printSecMarker, printData, printPressure, printIndexRcd, printSysRcd
};


/************************************************************************/
/* Function	   : printFileHdr											*/
/* Purpose	   : Print contents of file header							*/
/* Inputs	   : Ptr to FILE											*/
/* Outputs	   : None													*/
/************************************************************************/
Void printFileHdr(FILE *fp)
{
	char		b;
	time_t		startTime;
	struct tm	*tmp;
	Nat16		startMs;

	fread(&b, 1, 1, fp);
	printf("FileHdr ver=%hu ", readWord(fp));
	printf("ID=%u ", readWord(fp));
	printf("rate=%u ", readWord(fp));

	startTime = readLong(fp);
	tmp = gmtime(&startTime);
	startMs = readWord(fp);
	duration = readLong(fp);

	printf("%02d/%02d/%04d %02d:%02d:%02d.%03hd dur=%ld.%03lu\n",
		   tmp->tm_mon+1, tmp->tm_mday, tmp->tm_year+1900,
		   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
		   startMs, duration/1000, duration%1000);
}


/************************************************************************/
/* Function	   : checkDataRecs											*/
/* Purpose	   : See if we need to print number of data recs found		*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal Void checkDataRecs(Void)
{
	if (!printDataRecs && !totalsOnly && !pressOnly && !sysRecOnly && numDataRecs)
	{
		printf("%lu Data Records\n", numDataRecs);
		numDataRecs = 0;
	}
}

/************************************************************************/
/* Function	   : printSecMarker											*/
/* Purpose	   : Print second marker record								*/
/* Inputs	   : Ptr to FILE											*/
/* Outputs	   : None													*/
/************************************************************************/
Void printSecMarker(FILE *fp)
{
	time_t		secTime;
	struct tm	*tmp;
	char		b;

	checkDataRecs();
	fread(&b, 1, 1, fp);
	secTime = readLong(fp);
	tmp = gmtime(&secTime);

	if (!totalsOnly && !pressOnly && !sysRecOnly)
		printf("Second Marker %02d/%02d/%04d %02d:%02d:%02d\n",
			   tmp->tm_mon+1, tmp->tm_mday, tmp->tm_year+1900,
			   tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
}

/************************************************************************/
/* Function	   : printData												*/
/* Purpose	   : Print data record										*/
/* Inputs	   : Ptr to FILE											*/
/* Outputs	   : None													*/
/************************************************************************/
Void printData(FILE *fp)
{
	Byte		b[22], *p;
	long		lval, i;
	double		accel[3], quat[4], norm;

	fread(b, 4*QUAT_SIZE + 9, 1, fp);
	totDataRecs++;

	if (!printDataRecs || totalsOnly || pressOnly || sysRecOnly)
		numDataRecs++;
	else
	{
		for (i = 0, p = b; i < 3; i++)
		{
			lval = (long)(((unsigned long)p[0] << 16) +
						  ((unsigned long)p[1] << 8) +
						  ((unsigned long)p[2]));
			if (lval & 0x800000)
				lval |= ~0xffffffL;
			accel[i] = (double)lval / 65536.0f;
			p += 3;
		}

		for (i = 0; i < 4; i++)
		{
#if QUAT_SIZE == 2
			Int16 ival = (Int16)(((unsigned long)p[0] << 8) +
								 (unsigned long)p[1]);
			quat[i] = (double)ival / 16384.0f;
			p += 2;
#else
			lval = (long)(((unsigned long)p[0] << 24) +
						  ((unsigned long)p[1] << 16) +
						  ((unsigned long)p[2] << 8));
			quat[i] = (double)lval / 1073741824.0f;
			p += 3;
#endif
		}

#ifdef CFX
		printf("Inertial Data  acc: %8.5f %8.5f %8.5f  quat: %8.5f %8.5f %8.5f %8.5f\n",
			   accel[0], accel[1], accel[2], quat[0], quat[1], quat[2], quat[3]);
#else
		norm = sqrt(quat[0]*quat[0] + quat[1]*quat[1] + quat[2]*quat[2] + quat[3]*quat[3]);

		if ((norm >= 1.0-epsilon) && (norm <= 1.0+epsilon))
			printf("Inertial Data  acc: %8.5f %8.5f %8.5f  quat: %8.5f %8.5f %8.5f %8.5f norm: %9.6f\n",
				   accel[0], accel[1], accel[2], quat[0], quat[1], quat[2], quat[3], norm);
		else
			deletedDataRecs++;
#endif	
	}
}

/************************************************************************/
/* Function	   : printPressure											*/
/* Purpose	   : Print pressure record									*/
/* Inputs	   : Ptr to FILE											*/
/* Outputs	   : None													*/
/************************************************************************/
Void printPressure(FILE *fp)
{
	Byte		b;
	Nat16		press;
	
	checkDataRecs();
	fread(&b, 1, 1, fp);
	press = readWord(fp);

	if (!totalsOnly && !sysRecOnly)
		printf("Ext Pressure %hu counts %5.3f volts %6.3f bar\n",
			   press, RAW_TO_VOLTS(press),
			   adcEngValue(EXT_PRESS_CHAN, press, NULL));
}

/************************************************************************/
/* Function	   : printIndexRcd											*/
/* Purpose	   : Print index record										*/
/* Inputs	   : Ptr to FILE											*/
/* Outputs	   : None													*/
/************************************************************************/
Void printIndexRcd(FILE *fp)
{
	time_t		startTime;
	struct tm	*tmp;
	Nat16		startMs;
	Nat32		idxDuration;
	Flt32		maxVal;
	Byte		b[16];

	checkDataRecs();
	fread(b, 1, 1, fp);

	startTime = readLong(fp);
	tmp = gmtime(&startTime);
	startMs = readWord(fp);
	idxDuration = readLong(fp);
	maxVal = (float)readLong(fp) / 65536.0f;

	if (!totalsOnly)
		printf("Index %02d/%02d/%04d %02d:%02d:%02d.%03hd dur= %3ld.%03lu maxVal= %.4f ",
			   tmp->tm_mon+1, tmp->tm_mday, tmp->tm_year+1900,
			   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
			   startMs, idxDuration/1000, idxDuration%1000, maxVal);

	fread(b, 16, 1, fp);
	b[15]='\0';

	if (!totalsOnly)
		printf("file=%s\n", b);
}

/************************************************************************/
/* Function	   : printSysRcd											*/
/* Purpose	   : Print system data record								*/
/* Inputs	   : Ptr to FILE											*/
/* Outputs	   : None													*/
/************************************************************************/
Void printSysRcd(FILE *fp)
{
	Byte		b[4];
	time_t		srTime;
	struct tm	*tmp;
	Nat16		batt, vmodem, epress, ipress, temp, hum;

	checkDataRecs();
	fread(b, 1, 1, fp);
	srTime = readLong(fp);
	tmp = gmtime(&srTime);
	batt = readWord(fp);
	vmodem = readWord(fp);
	epress = readWord(fp);
	ipress = readWord(fp);
	temp = readWord(fp);
	hum = readWord(fp);

	if (!totalsOnly && !pressOnly)
	{
		printf("Sys Rcd %02d/%02d/%04d %02d:%02d:%02d Batt %5.2fV "
			   "Vmodem %5.2fV ExtPress %6.3f bar IntPress %5.2f psia "
			   "Temp %4.1f degC Hum %4.1f%%\n",
			   tmp->tm_mon+1, tmp->tm_mday, tmp->tm_year+1900,
			   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
			   RAW_TO_BATTVOLTS(batt), RAW_TO_BATTVOLTS(vmodem),
			   adcEngValue(EXT_PRESS_CHAN, epress, NULL),
			   adcEngValue(INT_PRESS_CHAN, ipress, NULL),
			   adcEngValue(TEMP_CHAN, temp, NULL),
			   adcEngValue(HUMIDITY_CHAN, hum, NULL));
#ifndef CFX
		printf("Ext Pressure %hu counts %5.3f volts %6.3f bar\n",
			   epress, RAW_TO_VOLTS(epress),
			   adcEngValue(EXT_PRESS_CHAN, epress, NULL));
#endif
	}
}


/************************************************************************/
/* Function	   : decodeFile												*/
/* Purpose	   : Decode data file										*/
/* Inputs	   : File name												*/
/* Outputs	   : OK, ERROR, or -2 for user interruption					*/
/************************************************************************/
Errno decodeFile(char *fname)
{
	int			recType;
	FILE		*fp;

	if ((fp = fopen(fname, "r")) == NULL)
	{
		printf("Could not open file %s\n", fname);
		return(ERROR);
	}

	printf("\nDecoding file %s:\n", fname);
	totDataRecs = deletedDataRecs = 0;

	while((recType = fgetc(fp)) != EOF)
	{
		if (recType < NumberOf(rcdFunc))
			(*rcdFunc[recType])(fp);
		else
			printf("Unknown Record type: %d\n", recType);
#ifdef CFX
		if (kbhit())
			return(-2);
#endif
	}

	checkDataRecs();
	fclose(fp);

	if (totDataRecs > 0)
		printf("File %s: %lu data records in %ld.%03lu seconds\n",
			   fname, totDataRecs, duration/1000, duration%1000);

	if (deletedDataRecs > 0)
		printf("\nDeleted %lu invalid data records\n\n", deletedDataRecs);

	return(OK);

} /* decodeFile() */


#ifdef CFX						/* For embedded version					*/

/************************************************************************/
/* Function	   : dsDecodeFile											*/
/* Purpose	   : Func sent to dirScan to decode one file				*/
/* Inputs	   : dirent ptr (per dirScan)								*/
/* Outputs	   : TRUE to continue, FALSE to stop						*/
/************************************************************************/
MBool dsDecodeFile(struct dirent *de)
{
	if ((de->d_attr & (_A_SUBDIR | _A_VOLID)) == 0)
		return(decodeFile(de->d_name) != -2);

	return(TRUE);

} /*dsDecodeFile() */


/************************************************************************/
/* Function	   : DecodeCmd												*/
/* Purpose	   : Command to decode data files							*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char *DecodeCmd(CmdInfoPtr cip)
{
	int			index;

	DosSwitch	qsw = { "/-", '?', 0, 0 };
	DosSwitch	dsw = { "/-", 'D', 0, 0 };
	DosSwitch	tsw = { "/-", 'T', 0, 0 };
	DosSwitch	psw = { "/-", 'P', 0, 0 };
	DosSwitch	ssw = { "/-", 'S', 0, 0 };

	CmdExtractCIDosSwitches(cip, fiveArgs, &qsw, &dsw, &tsw, &psw, &ssw);
	index = argIndex(5, &qsw, &dsw, &tsw, &psw, &ssw);
	printDataRecs = totalsOnly = pressOnly = sysRecOnly = FALSE;

// HELP SWITCH SELECTED (common format to many commands)
	if (qsw.pos)
	{
		printf("\nDECODE [/D] [/T] <filename>\tDecode a BEDS binary file\n"
			   "\t[/D] Print full data records (else prints summaries each second)\n"
			   "\t[/T] Print only totals, not individual records (except header)\n"
			   "\t[/S] Print only System Records\n"
			   "\t[/P] Print only Pressure Records\n");

		return(NULL);
	}

	if (dsw.pos)
		printDataRecs = TRUE;

	if (tsw.pos)
		totalsOnly = TRUE;

	if (psw.pos)
		pressOnly = TRUE;

	if (ssw.pos)
		sysRecOnly = TRUE;

	if (index >= cip->argc)
		return("No input file specified");

	for( ; index < cip->argc; index++)
		if (!dirScan(cip->argv[index].str, dsDecodeFile, NULL))
			return(NULL);

	return(NULL);
							  
} /* DecodeCmd() */

#endif
