/****************************************************************************/
/* Summary  : BEDS command to decode binary files			    */
/* Filename : decode.c                                                      */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System			    */
/* Revision : 1.0							    */
/* Created  : 11/10/2011						    */
/****************************************************************************/
/* Modification History:						    */
/* 10nov2011 rah - created						    */
/****************************************************************************/

#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));
    }
}


/************************************************************************/
/* 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
