/****************************************************************************/
/* Summary  : File I/O routines for BEDS				    */
/* Filename : file.c							    */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System)			    */
/* Revision : 1.0							    */
/* Created  : 10/02/2012						    */
/****************************************************************************/
/* Modification History:						    */
/* 02oct2012 rah - created						    */
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>		/* MBARI type definitions		*/
#include <mbariConst.h>		/* MBARI constants			*/
#include <beds.h>		/* BEDS general definitions		*/
#include <file.h>		/* BEDS file I/O definitions		*/
#include <io.h>			/* BEDS I/O definitions			*/
#include <clock.h>		/* BEDS Clock module			*/
#include <syslog.h>		/* System logger			*/
#include <fileUtils.h>		/* BEDS file utilities definitions	*/

#include <cfxbios.h>		// Persistor BIOS and I/O Definitions
#include <cfxpico.h>		// Persistor PicoDOS Definitions
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <dirent.h>


/********************************/
/*	External Data		*/
/********************************/

Extern Int32	platformID;
Extern Int32	deploymentID;


/********************************/
/*	Global Data		*/
/********************************/

Global const char	*watchIdxName = "WATCH.IDX";
Global const char	*eventIdxName = "EVENT.IDX";
Global const char	*clkFileName  = "BEDS.CLK";


/************************************************************************/
/* Function    : writeFileHdr						*/
/* Purpose     : Write a file header record				*/
/* Inputs      : Open file ptr, rate of primary data in samples/min	*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
MLocal Errno writeFileHdr(FILE *fp, Nat16 dataRate)
{
    FileHdr	hdr;
    size_t	res;

    hdr.recType = FileHdrType;
    hdr.rsvd = 0;
    hdr.fmtVersion = FMT_VERSION;
    hdr.platformID = (Nat16)(platformID & 0xffff);
    hdr.dataRate = dataRate;
    clkGetTime(&hdr.startTime, &hdr.startMs);
    hdr.duration = 0;
    res = fwrite(&hdr, sizeof(hdr), 1, fp);
    return(res <= 0 ? ERROR : OK);

} /* writeFileHdr() */


/************************************************************************/
/* Function    : fileInit						*/
/* Purpose     : Initialize file module, create data subdirectory	*/
/* Inputs      : None							*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
Errno fileInit(void)
{
    FILE	*fp, *driftfp;
    Errno	rtn = OK;
    static char	*openType = "w";

    if (!CFCardDetect())
    {
	printf("\nNo Compact Flash card detected!\n");
	return(ERROR);
    }

    execstr("SET SYS.F32TRUST=*");

    if (!PZCacheSetup(CFM_DRV, calloc, free))
	return(ERROR);

    backupFile(clkFileName, "BEDSCLK.%03d");
    backupFile(eventIdxName, "EVENTIDX.%03d");
    backupFile(watchIdxName, "WATCHIDX.%03d");

    if ((driftfp = fopen(clkFileName, openType)) != NULL)
	fclose(driftfp);
    else
	rtn = ERROR;

    if ((fp = fopen(eventIdxName, openType)) != NULL)
    {
	writeFileHdr(fp, 0);
	fclose(fp);
    }
    else
	rtn = ERROR;
	
    if ((fp = fopen(watchIdxName, openType)) != NULL)
    {
	writeFileHdr(fp, 0);
	fclose(fp);
    }
    else
	rtn = ERROR;

    return(rtn);

} /* fileInit() */


/************************************************************************/
/* Function    : fileClose						*/
/* Purpose     : Terminate this module					*/
/* Inputs      : None							*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
Errno fileClose(Void)
{
    PZCacheRelease(CFM_DRV);
    return(OK);
}


/************************************************************************/
/* Function    : getDataFileName					*/
/* Purpose     : Get the file name of a data file			*/
/* Inputs      : File number, file suffix, buffer to place name into	*/
/* Outputs     : None							*/
/************************************************************************/
void getDataFileName(long fileNum, char *suffix, char *nameBuf)
{
   char	platformChar;

    if ((platformID < 0) || (platformID > 35))
	platformChar = '_';
    else if (platformID < 10)
	platformChar = platformID + '0';
    else
	platformChar = platformID - 10 + 'A';

    sprintf(nameBuf, "%c%02X%05d.%s",
	    platformChar, (deploymentID & 0xff), fileNum, suffix);

} /* getDataFileName() */


/************************************************************************/
/* Function    : openNewDataFile					*/
/* Purpose     : Open a new data file					*/
/* Inputs      : DataFileType, data rate in samples/minute		*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
Errno openNewDataFile(DataFileStruct *dfp)
{
    FILE	*fp, *idxfp;
    IdxRcd	indexRcd;
    FileHdr	hdr;
    Errno	rtn;

    if (dfp->ppbBuf == NULL)			/* If no PPB buffer, error*/
	return(ERROR);

    if ((dfp->ppb = PPBOpen(PPB_SIZE, dfp->ppbBuf, 0, 0, 0)) == NULL)
	return(ERROR);

    getDataFileName(dfp->fileIndex, dfp->fileSuffix, dfp->fileName);

    dfp->fileIndex++;				/* Incr file number	 */
    sysLogPrintf("Opening data file %s", dfp->fileName);

    if ((fp = fopen(dfp->fileName, "w")) == NULL)
	return(ERROR);				/* Open data file to clear it*/

    fclose(fp);					/* And close it again	*/

    if ((idxfp = fopen(dfp->indexFile, "a")) == NULL)
	return(ERROR);				/* Open Index file	*/

    hdr.recType = FileHdrType;			/* Create File Hdr	*/
    hdr.rsvd = 0;
    hdr.fmtVersion = FMT_VERSION;
    hdr.platformID = (Nat16)(platformID & 0xffff);
    hdr.dataRate = dfp->dataRate;
    clkGetTime(&hdr.startTime, &hdr.startMs);
    hdr.duration = 0;
    PPBWrite(dfp->ppb, &hdr, sizeof(hdr));	/* Write file hdr to PPB */	

    dfp->indexRecOffset = ftell(idxfp);		/* Remember where we put idx rec*/
    indexRcd.recType = IndexType;		/* Create Index record	 */
    indexRcd.rsvd = 0;
    indexRcd.startTime = hdr.startTime;
    indexRcd.startMs = hdr.startMs;
    indexRcd.duration = 0;
    indexRcd.maxVal = 0;
    strncpy(indexRcd.fileName, dfp->fileName, 16);
    rtn = (fwrite(&indexRcd, sizeof(indexRcd), 1, idxfp) < 1) ? ERROR : OK;
    fclose(idxfp);				/* Close index file	 */
    return(rtn);

} /* openNewDataFile() */


/************************************************************************/
/* Function    : checkDataFile						*/
/* Purpose     : See if it's time to write PPB to data file		*/
/* Inputs      : DataFileStruct						*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
Errno	checkDataFile(DataFileStruct *dfp)
{
    Int32	ppbAvail;
    void	*ppbPtr;
    FILE	*fp;
    Errno	rtn;

    if (dfp->ppb == NULL)
	return(ERROR);

    if ((ppbAvail = PPBCheckRdAvail(dfp->ppb)) < PPB_SIZE/2)
	return(OK);

    if ((ppbPtr = PPBGetMemBuf(dfp->ppb, &ppbAvail, TRUE)) == NULL)
	return(ERROR);

    if ((fp = fopen(dfp->fileName, "a")) == NULL)
	return(ERROR);

    rtn = (fwrite(ppbPtr, ppbAvail, 1, fp) < 1) ? ERROR : OK;

    fclose(fp);
    return(rtn);

} /* checkDataFile() */


/************************************************************************/
/* Function    : closeDataFile						*/
/* Purpose     : Close the Event or Watch data file			*/
/* Inputs      : DataFileStruct						*/
/* Outputs     : Errno							*/
/************************************************************************/
Errno	closeDataFile(DataFileStruct *dfp)
{
    Int32	ppbAvail;
    void	*ppbPtr;
    FileHdr	hdr;
    FILE	*fp, *ifp;
    time_t	now;
    Nat16	ms;
    Nat32	duration = 0;
    Errno	rtn = OK;

    if ((dfp->ppb == NULL) ||			/* Open the data file*/
	((fp = fopen(dfp->fileName, "r+")) == NULL))
	return(ERROR);

    PPBFlush(dfp->ppb);				/* Flush the PPB	*/
						/* and write it to file */
    if ((ppbAvail = PPBCheckRdAvail(dfp->ppb)) > 0)
	if ((ppbPtr = PPBGetMemBuf(dfp->ppb, &ppbAvail, TRUE)) != NULL)
	{
	    fseek(fp, 0L, SEEK_END);
	    fwrite(ppbPtr, ppbAvail, 1, fp);
	}

    clkGetTime(&now, &ms);		/* Get current time plus ms	*/
    rewind(fp);				/* Rewind to beginning of data file*/
    if (fread(&hdr, sizeof(hdr), 1, fp) >= 1) /* Read back the file header*/
    {
	duration = 1000L * (now - hdr.startTime) + ms - hdr.startMs;
	fseek(fp, OffsetOf(FileHdr, duration), SEEK_SET);
	fwrite(&duration, sizeof(Nat32), 1, fp);
    }
    else
	rtn = ERROR;

    fclose(fp);				/* Close data file		*/
    PPBClose(dfp->ppb);			/* Close Ping-pong buffer	*/
    dfp->ppb = NULL;

    if ((ifp = fopen(dfp->indexFile, "r+")) == NULL)
	return(ERROR);			/* Open index file		*/
					/* Write duration and maxVal	*/
    fseek(ifp, dfp->indexRecOffset + OffsetOf(IdxRcd, duration), SEEK_SET);
    fwrite(&duration, sizeof(Nat32), 1, ifp);
    fwrite(&dfp->maxVal, sizeof(Int32), 1, ifp);

    fclose(ifp);			/* Close index file		*/
    return(rtn);

} /* closeDataFile() */


/************************************************************************/
/* Function    : writeClkDrift						*/
/* Purpose     : Write an entry into the clock drift file		*/
/* Inputs      : None							*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
Errno writeClkDrift(Void)
{
    FILE	*driftfp;
    Int32	dat[2];
    Errno	rtn;

    if ((driftfp = fopen(clkFileName, "a")) == NULL)
	return(ERROR);

    dat[0] = clkGetAccurateTime();
    dat[1] = clkGetTimeOffset();

    rtn = (fwrite(dat, sizeof(Int32), 2, driftfp) < 2) ? ERROR : OK;

    fclose(driftfp);
    return(rtn);

} /* writeClkDrift() */


/************************************************************************/
/* Function    : writeSecondMarker					*/
/* Purpose     : Write a second marker into watch or event data file	*/
/* Inputs      : Ping-pong buf to write record into, current time	*/
/* Outputs     : OK or ERROR						*/
/************************************************************************/
Errno writeSecondMarker(Void *ppb, time_t secs)
{
    SecondMarker sm;

    if (ppb == NULL)
	return(ERROR);

    sm.recType = SecMarkerType;
    sm.rsvd = 0;
    sm.rcdTime = secs;
    PPBWrite(ppb, &sm, sizeof(sm));
    return(OK);

} /* writeSecondMarker() */
