/* *****************************************************
 * *                                                   *
 * *  Support routines for IrisToBufr and BufrToIris   *
 * *                                                   *
 * *****************************************************
 * File: utils/grib1/GribSubs.C
 *
 *               COPYRIGHT (c) 2002, 2003, 2004, 2006 BY
 *             VAISALA INC., WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */

/* This program is a pipe which takes an IRIS product in the standard input
 * and writes a modified product out the standard output.
 *
 * Note if you are modifying this program, and wish to insert a diagnostic
 * printf, be sure to write it to the error output.
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C" {
  /*#include "desc.h"
  */
}

#include "sigtypes.h"
#include "user_lib.h"

#include "siggrib1.h"

static UINT2 ProcessConfigLine
(char *sline_a,
 struct convert_config8 *pConfig_a );

/* ============================================================
 * Function to load the convert setup from a configuration file.
 */
void LoadSetup
( struct convert_config8 *pCSetup_a,
  const char* sFile_a,                  /* Filename of config file */
  int iDevice_a )                       /* Device number */
{
  char sPath[PATHNAME_SIZE];
  char sline[81], *sptr;
  FILE* stream;
  SINT4 iline;
  UINT2 lerror;

  /* Initialize the data to the default values */
  memset( pCSetup_a, 0, sizeof(*pCSetup_a) );
  pCSetup_a->fMinReflectivity = -1000.0;

  /* Default logging to file */
  pCSetup_a->lLogToFile=TRUE;

  /* Generate the file name and open the file */
  sptr = getenv( "IRIS_CONFIG" );
  if (NULL == sptr) 
    {
      sig_signal_va( USER_NO_ENV, 1, "IRIS_CONFIG" );
      exit(EXIT_FAILURE);
    }

  strncpy( sPath, sptr, DIRECTORY_SIZE-1 );
  sPath[DIRECTORY_SIZE-1] = 0;
  strcat( sPath, sFile_a );
  stream = fopen( sPath, "r" );

  /* Don't worry about a missing config file */
  if (NULL == stream) return;

  /* now loop through the file reading and parsing lines */
  iline = 1;
  sptr = fgets( sline, 81, stream );
  while (NULL != sptr)
    {
      lerror = ProcessConfigLine( sptr, pCSetup_a );
      if (lerror) fprintf( stderr, "Syntax error on .conf file line %d\n", iline );
      iline +=1;
      sptr = fgets( sline, 81, stream );
    }

  fclose( stream );

  return;
}

/* ===============================================================
 * Process one line from the configuration file.  Returns nonzero if there
 * is a syntax error.
 *
 * The config can contain the following commands.  All blank lines and
 * lines beginning with "#" are ignored.  Commands can be in any order.
 * Commands take effect in the order received.  For example, syntax errors
 * before the LOG command will not be logged.
 *
 * LOG              <FILE TERM BOTH NONE>
 * MIN_REFLECTIVITY #
 * PROJECT_NAME     <name>
 * VERBOSE
 */
static UINT2 ProcessConfigLine
(char *sline_a,
 struct convert_config8 *pConfig_a )
{
  char *stokens[9], *sptr;
  UINT4 icount, ioffset;
  
  ioffset = 0;
  icount  = 0;
  /* Process up to 9 tokens, stop when the line is exhausted. */
  while (icount<9) 
    {
      stokens[icount] = sigtok( sline_a, &ioffset );
      if (NULL == stokens[icount])
	break;
      else
	icount++;
    }
  
  /* Blank lines are allowed and skipped */
  if (icount == 0) return FALSE;
  
  /* Skip commented lines */
  if (stokens[0][0] == '#') return FALSE;
  
  if ( 0 == strcmp( "APPEND_TO_LOG", stokens[0] ) )
    {
      pConfig_a->lAppendToLog=TRUE;
      return FALSE;
    }

  /* Set various ID related values */
  if ( 0 == strcmp( "ID", stokens[0] ) )
    {
      SINT4 iValue;
      if ( icount < 3 ) return TRUE;
      
      iValue = strtol( stokens[2], &sptr, 10 );
      if (sptr == stokens[2]) return TRUE;

      if (iValue <   0 ) return TRUE;
      if (iValue > 255 ) return TRUE;

      if ( 0 == strcmp( "CENTER", stokens[1] ) )
	{
	  pConfig_a->iGenCenterId = iValue;
	}
      else if ( 0 == strcmp( "PROCESS", stokens[1] ) )
	{
	  pConfig_a->iGenProcessId = iValue;
	}
      else if ( 0 == strcmp( "SUBCENTER", stokens[1] ) )
	{
	  pConfig_a->iSubCenterId = iValue;
	}
      return FALSE;
    }

  /* See if the line contains the LOG file command */
  if ( 0 == strcmp( "LOG", stokens[0] ) )
    {
      if ( icount < 2 ) return TRUE;
      
      if      ( 0 == strcmp( "BOTH", stokens[1] ) ) 
	{pConfig_a->lLogToFile=TRUE;  pConfig_a->lLogToTerm=TRUE;}
      else if ( 0 == strcmp( "FILE", stokens[1] ) ) 
	{pConfig_a->lLogToFile=TRUE;  pConfig_a->lLogToTerm=FALSE;}
      else if ( 0 == strcmp( "TERM", stokens[1] ) )
	{pConfig_a->lLogToFile=FALSE; pConfig_a->lLogToTerm=TRUE;}
      else if ( 0 == strcmp( "NONE", stokens[1] ) )
	{pConfig_a->lLogToFile=FALSE; pConfig_a->lLogToTerm=FALSE;}
      else
	return TRUE;
      return FALSE;
    }

  /* Set the minimum refectivity */
  if ( 0 == strcmp( "MIN_REFLECTIVITY", stokens[0] ) )
    {
      FLT8 fValue;
      if ( icount < 2 ) return TRUE;
      
      fValue = strtod( stokens[1], &sptr );
      if (sptr == stokens[1]) return TRUE;

      if (fValue < -100.0 ) return TRUE;
      if (fValue >  100.0 ) return TRUE;

      pConfig_a->fMinReflectivity = fValue;
      return FALSE;
    }

  /* Set various table 2 values */
  if ( 0 == strcmp( "TABLE2", stokens[0] ) )
    {
      SINT4 iValue; double fValue;
      struct convert_datatype *pData;
      if ( icount < 6 ) return TRUE;

      if ( 0 == strcmp( "DBZ", stokens[1] ) )
	{
	  pData = &pConfig_a->Dbz;
	}
      else if ( 0 == strcmp( "HEIGHT", stokens[1] ) )
	{
	  pData = &pConfig_a->Height;
	}
      else if ( 0 == strcmp( "RAINRATE", stokens[1] ) )
	{
	  pData = &pConfig_a->RainRate;
	}
      else if ( 0 == strcmp( "RAINACC", stokens[1] ) )
	{
	  pData = &pConfig_a->RainAcc;
	}
      else if ( 0 == strcmp( "VELOCITY", stokens[1] ) )
	{
	  pData = &pConfig_a->Vel;
	}
      else if ( 0 == strcmp( "WIDTH", stokens[1] ) )
	{
	  pData = &pConfig_a->Width;
	}
      else if ( 0 == strcmp( "ZDR", stokens[1] ) )
	{
	  pData = &pConfig_a->Zdr;
	}
      else
	{
	  return TRUE;
	}

      /* Third token is the code value */
      iValue = strtol( stokens[2], &sptr, 10 );
      if (sptr == stokens[2]) return TRUE;
      if (iValue <   0 ) return TRUE;
      if (iValue > 255 ) return TRUE;
      pData->iCode = iValue;

      /* Fourth token is the GRIB scale value */
      fValue = strtod( stokens[3], &sptr );
      if (sptr == stokens[3]) return TRUE;
      pData->fGribScaleFactor = fValue;

      /* Fifth token is the Decimal scale value */
      iValue = strtol( stokens[4], &sptr, 10 );
      if (sptr == stokens[4]) return TRUE;
      if (iValue < -100 ) return TRUE;
      if (iValue >  100 ) return TRUE;
      pData->iDecScaleFactor = iValue;

      /* Sixth token is the Binary scale value */
      iValue = strtol( stokens[5], &sptr, 10 );
      if (sptr == stokens[5]) return TRUE;
      if (iValue < -100 ) return TRUE;
      if (iValue >  100 ) return TRUE;
      pData->iBinScaleFactor = iValue;

      return FALSE;
    }

  if ( 0 == strcmp( "VERBOSE", stokens[0] ) )
    {
      pConfig_a->lVerbose = TRUE;
      return FALSE;
    }

#if 0
  /* Read an entry in the site table.
   * Syntax is: SITE <WMO block> <WMO Station> <IRIS Sitename> <IRIS Sitenumber> <minutes west> <lat> <lon> */
  if ( 0 == strcmp( "SITE", stokens[0] ) )
    {
      long iValue;
      struct convert_site_table8 *pOneSite;
      if ( icount < 8 ) return TRUE;
      if (pConfig_a->iSiteCount >= MAX_CONVERT_SITES) return TRUE;

      pOneSite = &(pConfig_a->Sites[pConfig_a->iSiteCount]);
      
      /* Read Block Number */
      iValue = strtol( stokens[1], &sptr, 10 );
      if (sptr == stokens[1]) return TRUE;
      /* Block number is Unsigned 7-bit value */
      if (iValue < 0 ) return TRUE;
      if (iValue > 127 ) return TRUE;
      pOneSite->iBlockNumber = iValue;
      
      /* Read Station Number */
      iValue = strtol( stokens[2], &sptr, 10 );
      if (sptr == stokens[2]) return TRUE;
      /* Block number is Unsigned 10-bit value */
      if (iValue < 0 ) return TRUE;
      if (iValue > 1023 ) return TRUE;
      pOneSite->iStationNumber = iValue;
      
      /* Read Site Name */
      strncpy( pOneSite->sSitename16, stokens[3], 16 );
      pOneSite->sSitename16[16] = 0;
            
      /* Read Site Number */
      pOneSite->iSiteNumber = strtol( stokens[4], &sptr, 10 );
      if (sptr == stokens[4]) return TRUE;
      /* Block number is Unsigned 10-bit value */
      if (pOneSite->iSiteNumber <  0 ) return TRUE;
      if (pOneSite->iSiteNumber > 30 ) return TRUE;

      /* Read Time Zone */
      pOneSite->iMinutesWest = strtol( stokens[5], &sptr, 10 );
      if (sptr == stokens[5]) return TRUE;
      /* Allow 16 hours either way */
      if (pOneSite->iMinutesWest < -16*60 ) return TRUE;
      if (pOneSite->iMinutesWest >  16*60 ) return TRUE;

      /* Read Latitude */
      pOneSite->Center.fLat = (FPI/180.0)*strtod( stokens[6], &sptr );
      if (sptr == stokens[6]) return TRUE;
      /* Allow +/- PI/2 */
      if (pOneSite->Center.fLat < -FPI/2.00 ) return TRUE;
      if (pOneSite->Center.fLat >  FPI/2.00 ) return TRUE;

      /* Read Longitude */
      pOneSite->Center.fLon = (FPI/180.0)*strtod( stokens[7], &sptr );
      if (sptr == stokens[7]) return TRUE;
      /* Allow +/- PI */
      if (pOneSite->Center.fLon < -FPI ) return TRUE;
      if (pOneSite->Center.fLon >  FPI ) return TRUE;

      pConfig_a->iSiteCount++;
      return FALSE;
    }
#endif  /* #if 0 */

  /* Unrecognized lines will generate a syntax error */
  return TRUE;
}

/* ===========================================
 * Print string to log and terminal, if desired.
 */
void Lprintf
( const struct convert_config8 *pCSetup_a,
  const char *sString_a )
{

  if (pCSetup_a->lLogToTerm) fprintf( stderr, "%s\n", sString_a );
  if (pCSetup_a->lLogToFile)
    {
      struct ymds_time Ymds;
      char sTimeBuf[TIMENAME_SIZE], sDateBuf[TIMENAME_SIZE];

      GetTimeYmds( &Ymds, TIME_FLG_LOCAL );
      
      shhmmss_r( Ymds.isec, sTimeBuf );
      sddmonyyyy_r( Ymds.iyear, Ymds.imon, Ymds.iday, sDateBuf );
      fprintf(pCSetup_a->iLogFile, "%s %s.%03d: %s\n", sDateBuf, sTimeBuf, 
	      MS_FROM_MILLS(Ymds.imills), sString_a);
      fflush( pCSetup_a->iLogFile );
    }
  return;
}


/* ===============================================================
 * Pack an integer into 4 octets
 */
void Pack4
( char iTarget_a[4], 
  int iValue_a )
{
  iTarget_a[3] = 255 & iValue_a;
  iTarget_a[2] = 255 & (iValue_a>>8);
  iTarget_a[1] = 255 & (iValue_a>>16);
  iTarget_a[0] = 255 & (iValue_a>>24);
  return;
}
/* ===============================================================
 * Pack an integer into 3 octets
 */
void Pack3
( char iTarget_a[3], 
  int iValue_a )
{
  iTarget_a[2] = 255 & iValue_a;
  iTarget_a[1] = 255 & (iValue_a>>8);
  iTarget_a[0] = 255 & (iValue_a>>16);
  return;
}
/* ===============================================================
 * Pack an integer into 2 octets
 */
void Pack2
( char iTarget_a[2], 
  int iValue_a )
{
  iTarget_a[1] = 255 & iValue_a;
  iTarget_a[0] = 255 & (iValue_a>>8);
  return;
}

/*
 * ===============================================================
 * Convert a local floating number to IBM format float.
 */

/* The code in this file is not copyright by SIGMET, Inc because credit 
 * for this function goes to the:
 *
 *                            GRIB LIBRARY v.3.0
 *                            ~~~~~~~~~~~~~~~~~~
 *      Developed at Science Applications International Corporation (SAIC)
 *                               Monterey, CA
 *
 *      Sponsored by Naval Research Laboratory (NRL), Marine Meteorology Division
 *                               Monterey, CA
 *
 *      with funding from the Defense Modeling and Simulation Office (DMSO) for
 *      the Master Environmental Library (MEL) and Environmental Effects in
 *      Distributed Interactive Simulation (E2DIS) projects.
 *
 *
 *            Developers:   Steve Lowe (SAIC) lowe@nrlmry.navy.mil
 *                          Louis Hembree (NRL)
 *                          Alice Nakajima (SAIC)
 */

void PackIbmFloat4
( char iTarget_a[4],
  double local_float)
{
  unsigned long ibm_float;
  
  if (local_float == 0.0)
    { 
      ibm_float = 0;
    } 
  else 
    {
      SINT4 a, b; 
      /*
       * B.1.b     ELSE
       *              CONVERT to IBM floating point
       *              ! IBM floating point is stored in 4 bytes as:  
       *              ! saaaaaaa bbbbbbbb bbbbbbbb bbbbbbbb
       *              ! where s is sign bit, 0 -> positive, 1 -> negative
       *              !       a is 7-bit characteristic
       *              !       b is 24-bit fraction
       *              ! s, a and b are obtained from local_float (local 32-bit float) as
       *              !       s = sign(local_float)
       *              !       a = ceil(log10(local_float) / log10(16.)) + 64 
       *              !       b = local_float / 16**(a-64) * 2**24
       * B.1.b     ENDIF
       */
      a = ((SINT4)ceil(log10(fabs(local_float)) / log10(16.))) + 64;
      /*      Local_float == +/-1. is a special case because of log function */
      if ( (local_float == 1.) || (local_float == -1.)) a = 65;
      b = (SINT4) (fabs(local_float) * pow(16.,(double) (70 - a)) +.5) & 0x00ffffff; 
      ibm_float = (((local_float > 0.) ? 0 : 1) << 31) | (a << 24) | b;
    }

  Pack4( iTarget_a, ibm_float );

}
/* ===================================================
 * Clear a bit in an array using GRIB standard bit order.
 */
void GribBitClear
( void *pBitMap_a,
  SINT4 iGribBit_a )
{
  SINT4 iIrisBit = 0xfffffff8 & iGribBit_a;
  iIrisBit |= 7-(7&iGribBit_a);

  bbclr_1( pBitMap_a, iIrisBit );

}
/* ===================================================
 * Assign a bit in an array using GRIB standard bit order.
 */
void GribBitPut
( void *pBitMap_a,
  SINT4 iGribBit_a,
  UINT1 iVal_a )
{
  SINT4 iIrisBit = 0xfffffff8 & iGribBit_a;
  iIrisBit |= 7-(7&iGribBit_a);

  bbput_1( pBitMap_a, iIrisBit, iVal_a );

}
