/************************************************************************/
/* Copyright 1993 - 1997 MBARI                                          */
/************************************************************************/
/* $Header: recordParse.c,v 1.0 93/09/30 09:51:28 hebo Exp $            */
/* Summary  : Parser Routines for Flight Recorder                       */
/* Filename : recordParse.c                                             */
/* Author   : Bob Herlien (rah)                                         */
/* Project  : New ROV                                                   */
/* $Revision: 1.0 $                                                     */
/* Created  : 09/22/99                                                  */
/************************************************************************/
/* Modification History:                                                */
/* $Log:        recordParse.c,v $
*/
/* 22sep97 rah, split off from recorder.c                               */
/************************************************************************/

#include <vxWorks.h>
#include <semLib.h>
#include <lstLib.h>
#include <sys/times.h>

#include <mbariTypes.h>
#include <datamgr.h>
#include <dm_errno.h>
#include <stdioLib.h>
#include <stdlib.h>
#include <string.h>

#include "recorder.h"


typedef struct                          /********************************/
{                                       /* Struct for parsing keywords  */
    char        *keyword;               /* Keyword to look for          */
    FUNCPTR     func;                   /* Function to call             */
} RecordParseStruct;                    /********************************/

#define RPP_NULL        ((RecordParseStruct *)NULL)

typedef struct                          /********************************/
{                                       /* Struct for DM Item tail      */
    char        *dmkeywd;               /* Keyword to look for          */
    Period      dmperiod;               /* DM Period to set (or NO_PERIOD)*/
    DWord       flags;                  /* Flags to OR in               */
} RecordParseDm;                        /********************************/


/********************************/
/*      Forward Declarations    */
/********************************/

Int32   recFileName( RecStruct *recPtr, char *tail );
Int32   recSetWrap( RecStruct *recPtr, char *tail );
Int32   recSetFlush( RecStruct *recPtr, char *tail );
Int32   recSetMaxFiles( RecStruct *recPtr, char *tail );


/********************************/
/*      Static Data             */
/********************************/

MLocal RecordParseStruct        recKeywds[] =
{ {"file", (FUNCPTR)recFileName}, {"wrap", (FUNCPTR)recSetWrap},
  {"flush", (FUNCPTR)recSetFlush}, {"maxfiles", (FUNCPTR)recSetMaxFiles}
};

MLocal RecordParseDm            recDmKeywds[] =
{ {"static", DM_STATIC, 0}, {"async", DM_ASYNC, 0},
  {"name", NO_PERIOD, REC_NAME}, {"data", NO_PERIOD, REC_DATA},
  {"date", NO_PERIOD, REC_DATE}, {"time", NO_PERIOD, REC_TIME},
  {"gmt", NO_PERIOD, REC_TIME_GMT}, {"always", NO_PERIOD, REC_ALWAYS}
};


/************************************************************************/
/* Function    : recordParseError                                       */
/* Purpose     : Print Initialization error for flight recorder         */
/* Inputs      : Ptr to error message                                   */
/* Outputs     : ERROR                                                  */
/************************************************************************/
     Int32
recordParseError( RecStruct *recPtr, char *msg )
{
    printf( "Flight Recorder initialization error near string:\n%s\n",
            recPtr->recbuff );
    if ( msg )
        printf( "%s\n", msg );

    return( ERROR );

} /* recordParseError() */


/************************************************************************/
/* Function    : recFindKeyword                                         */
/* Purpose     : Look for a keyword at beginning of recbuff             */
/* Inputs      : RecStruct Ptr                                          */
/* Outputs     : OK or ERROR                                            */
/************************************************************************/
     Int32
recFindKeyword( RecStruct *recPtr )
{
    Reg Nat32           i;
    Nat32               offset;
    char                keywd[RECBUFLEN];

    if ( sscanf(recPtr->recbuff, " %s%n", keywd, &offset) < 1 )
        return( ERROR );

    for ( i = 0; i < strlen(keywd); i++ )
        keywd[i] = tolower(keywd[i]);

    for ( i = 0; i < (sizeof(recKeywds) / sizeof(RecordParseStruct)); i++ )
        if ( strcmp(keywd, recKeywds[i].keyword) == 0 )
        {
            if ( (*recKeywds[i].func)(recPtr, recPtr->recbuff + offset) != OK )
                recordParseError( recPtr, NULL );
            offset = 0;
            return( OK );
        }

    return( ERROR );

} /* recFindKeyword() */


/************************************************************************/
/* Function    : recFileName                                            */
/* Purpose     : Set log file name for record task                      */
/* Inputs      : RecStruct ptr, tail of cfg file line                   */
/* Outputs     : SUCCESS or ERROR                                       */
/************************************************************************/
        Int32
recFileName( RecStruct *recPtr, char *tail )
{
    Reg char    *namebuf;
    char        filename[RECBUFLEN];

    if ( sscanf(tail, " %s", filename) < 1 )
        return( ERROR );

    if ( (namebuf = malloc((strlen(filename) + sizeof(int)) & ~sizeof(int)))
          == NULL )
        return( ERROR );

    strcpy( namebuf, filename );
    recPtr->fileName = namebuf;
    return( OK );

} /* recFileName() */


/************************************************************************/
/* Function    : recSetWrap                                             */
/* Purpose     : Set wrap time for record task                          */
/* Inputs      : RecStruct ptr, tail of cfg file line                   */
/* Outputs     : SUCCESS or ERROR                                       */
/************************************************************************/
        Int32
recSetWrap( RecStruct *recPtr, char *tail )
{
    Nat32       wraptime;

    if ( sscanf(tail, " %d", &wraptime) < 1 )
        return( ERROR );

    recPtr->wraptime = wraptime * sysClkRateGet();
    return( OK );

} /* recSetWrap() */


/************************************************************************/
/* Function    : recSetFlush                                            */
/* Purpose     : Set flush time for record task                         */
/* Inputs      : RecStruct ptr, tail of cfg file line                   */
/* Outputs     : SUCCESS or ERROR                                       */
/************************************************************************/
        Int32
recSetFlush( RecStruct *recPtr, char *tail )
{
    Nat32       flushtime;

    if ( sscanf(tail, " %d", &flushtime) < 1 )
        return( ERROR );

    recPtr->flushtime = flushtime * sysClkRateGet();
    return( OK );

} /* recSetFlush() */


/************************************************************************/
/* Function    : recSetMaxFiles                                         */
/* Purpose     : Set maxFiles for record task                           */
/* Inputs      : RecStruct ptr, tail of cfg file line                   */
/* Outputs     : SUCCESS or ERROR                                       */
/************************************************************************/
        Int32
recSetMaxFiles( RecStruct *recPtr, char *tail )
{
    Nat32       maxfiles;

    if ( sscanf(tail, " %d", &maxfiles) < 1 )
        return( ERROR );

    recPtr->maxFilePairs = maxfiles;
    return( OK );

} /* recSetMaxFiles() */


/************************************************************************/
/* Function    : recParseItemTail                                       */
/* Purpose     : Parse cmd tail for DM Item cfg line                    */
/* Inputs      : Ptr to cmd tail, ptrs to places to put period, flags   */
/* Outputs     : OK or ERROR                                            */
/************************************************************************/
        Void
recParseItemTail( char *tail, Period *dmPeriod, DWord *flags )
{
    Reg Nat32           i;
    Reg char            *p;

    for ( p = tail; *p; p++ )
        *p = tolower( *p );

    for ( i = 0; i < (sizeof(recDmKeywds) / sizeof(RecordParseDm)); i++ )
        if ( strstr(tail, recDmKeywds[i].dmkeywd) != NULL )
        {
            if ( recDmKeywds[i].dmperiod != NO_PERIOD )
                *dmPeriod = recDmKeywds[i].dmperiod;
            *flags |= recDmKeywds[i].flags;
        }

} /* recParseItemTail() */


/************************************************************************/
/* Function    : recordAddGroupItem                                     */
/* Purpose     : Free the items on recItemList and recGroupList         */
/* Inputs      : Ptr to RecStruct, Ptr to RecItem to add to group       */
/* Outputs     : SUCCESS or ERROR                                       */
/************************************************************************/
     Errno
recordAddGroupItem( RecStruct *recPtr, RecItem *rp )
{
    Reg NODE            *np;
    Reg RecGroup        *gp;
    Reg Errno           rtn;

    rp->rec_groupPtr = NO_GROUP;
    rp->rec_groupBit = 0;

    /* Try to add to an existing group          */

    for ( np = lstFirst(&recPtr->recGroupList);
          np != NODE_NULL; np = lstNext(np) )
    {
        gp = NODEtoRG( np );
        if ( dm_group_add_item(gp->grp_num, rp->rec_item, &rp->rec_groupBit)
             == SUCCESS )
        {
            rp->rec_groupPtr = gp;
            gp->grp_changes |= rp->rec_groupBit;        /* Forces 1st read */
            return( SUCCESS );
        }
    }

    /* No space in existing group.  Create a new group          */

    if ( (gp = (RecGroup *)malloc(sizeof(RecGroup))) == RG_NULL )
        return( ERROR );

    if ( (gp->grp_num = dm_create_group()) == NO_GROUP )
        return( ERROR );

    lstAdd( &recPtr->recGroupList, RGtoNODE(gp) );
    rp->rec_groupPtr = gp;
    rtn = dm_group_add_item(gp->grp_num, rp->rec_item, &rp->rec_groupBit);
    gp->grp_changes = rp->rec_groupBit;                 /* Forces 1st read */

    return( rtn );

} /* recordAddGroupItem() */


/************************************************************************/
/* Function    : recFindDmItem                                          */
/* Purpose     : See if cfg line is a reference to valid DM Item        */
/* Inputs      : RecStruct Ptr                                          */
/* Outputs     : OK or ERROR                                            */
/************************************************************************/
     Int32
recFindDmItem( RecStruct *recPtr )
{
    Reg Nat32   i;
    Reg char    *p;
    Reg RecItem *rp;
    Nat32       itemIndex, offset, offset1;
    Flt32       logPeriod;
    Period      dmPeriod;
    DM_Item     dmItem;
    DWord       itemFlags;
    MBool       isarray;
    char        token[RECBUFLEN];

    offset = 0;
    if ( sscanf(recPtr->recbuff, " %s%n", token, &offset) < 1 )
        return( ERROR );

    isarray = FALSE;
    itemIndex = 0;
    logPeriod = 0.0;
    dmPeriod = NO_PERIOD;
    itemFlags = 0;

    if ( (p = strchr(token, '[')) != NULL )
    {
        *p++ = '\0';
        if ( sscanf(p, "%d", &itemIndex) > 0 )
            isarray = TRUE;
    }

    if ( sscanf(recPtr->recbuff + offset, " %f%n", &logPeriod, &offset1) > 0 )
    {
        dmPeriod = (Period)(logPeriod * 1000000.0);
        offset += offset1;
    }

    recParseItemTail( recPtr->recbuff + offset, &dmPeriod, &itemFlags );

    if ( dmPeriod == NO_PERIOD )
        return(recordParseError(recPtr, "Missing or invalid period for item"));

    if ( (dmItem = dm_lookup(token, itemIndex)) == NO_ITEM )
        return( recordParseError(recPtr, "Cannot find item") );

    if ( (rp = (RecItem *)malloc(sizeof(RecItem))) == NULL )
        return( recordParseError(recPtr, "Out of space") );

    if ( dm_item_status(dmItem, &rp->rec_sts) != SUCCESS )
    {
        free( (char *)rp );
        return( recordParseError(recPtr, "Cannot get item status") );
    }

    if ( (rp->rec_data = malloc(rp->rec_sts.is_size)) == NULL )
    {
        dm_item_status_free( &rp->rec_sts );
        free( (char *)rp );
        return( recordParseError(recPtr, "Out of space") );
    }

    rp->rec_item = dmItem;
    rp->rec_tick = tickGet();
    rp->rec_period = (Nat32)(logPeriod * sysClkRateGet());
    rp->rec_arrayIndex = itemIndex;
    rp->rec_isArray = isarray;
    rp->rec_flags = itemFlags;
    if ( rp->rec_flags == 0 )
        rp->rec_flags = DFLT_FLAGS;

    if ( (dmPeriod == DM_STATIC) || (dmPeriod == DM_ASYNC) )
    {
        dm_start_consumer( dmItem, dmPeriod, recPtr->recSem );
        rp->rec_flags &= ~REC_ALWAYS;
    }
    else
        dm_start_consumer( dmItem, dmPeriod, SEM_NULL );

    if ( recordAddGroupItem(recPtr, rp) != OK )
    {
        dm_stop_consumer( dmItem );
        dm_item_status_free( &rp->rec_sts );
        free( rp->rec_data );
        free( (char *)rp );
        return( recordParseError(recPtr, "Couldn't add to group") );
    }

    lstAdd( &recPtr->recItemList, RItoNODE(rp) );

    return( OK );

} /* recFindDmItem() */


/************************************************************************/
/* Function    : recordParse                                            */
/* Purpose     : Parse Flight Recorder Configuration file               */
/* Inputs      : RecStruct Ptr                                          */
/* Outputs     : OK or ERROR                                            */
/************************************************************************/
     Int32
recordParse( RecStruct *recPtr )
{
    Reg FILE                    *cfgfp;

    if ( (cfgfp = fopen(recPtr->cfgFileName, "r")) == NULL )
    {
        printf( "Cannot open Flight Recorder config file %s\n",
               recPtr->cfgFileName );
        return( ERROR );
    }

    while ( fgets(recPtr->recbuff, RECBUFLEN, cfgfp) != NULL )
    {                                                   /* Read cfg file  */
        if ( recPtr->recbuff[0] != '#' )
            if ( recFindKeyword(recPtr) != OK )
                recFindDmItem( recPtr );

    } /* while */

    fclose( cfgfp );
    return( OK );

} /* recordParse() */
