/* ***************************************************************
 * *                                                             *
 * *  Input pipe to change the product name of an IRIS  product  *
 * *                                                             *
 * ***************************************************************
 * File: utils/in-pipes/ChProductName.C
 *
 *                         COPYRIGHT (c) 2002  BY
 *          SIGMET INCORPORATED, 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.  The modification
 * consists of opionally changing the product name.  It can easily be
 * extended to change the sitename also.  It will not work
 * on byte swapped products, currently.
 *
 * Note if you are modifying this program, and wish to insert a diagnostic
 * printf, be sure to write it to the error output.
 */

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

#include "sigtypes.h"
#include "headers.h"
#include "iris_task.h"
#include "ingest.h"
#include "product.h"

#include "misc_lib.h"
#include "user_lib.h"

/* This holds information to customize the convertor */
#define PRODUCT_CHANGE_MAX (30)
struct pname_table
{
  char  sOldSitename16[20];  /* Null terminated, wildcards allowed */
#define PTYPE_WILD ((UINT4)-1)
  UINT4 iOldPType;       /* Product type */
  char  sOldPName[16];   /* Null terminated, wildcards allowed */
  char  sNewPName[12];   /* Space padded */
};
struct convert_config
{
  UINT1 lLogToFile;
  UINT1 lLogToTerm;
  UINT1 lVerbose;             /* Log more details */
  UINT1 lAppendToLog;         /* Do not erase file each time */
  FILE *iLogFile;             /* Open file descriptor to log file */
  SINT4 iPNameCount;
  struct pname_table Table[PRODUCT_CHANGE_MAX];
};

struct convert_config CConfig_c;

/* Local function prototypes
 */
static void ChangeProductHeader
(struct product_hdr *phdr_a );

static void ProcessCommandLine
(int iArgc, 
 char *sArgv[],
 char sFilename_a[FILENAME_SIZE] );

static void Usage( void );

static void LoadSetup
( struct convert_config *pCSetup_a );

static UINT2 ProcessConfigLine
(char *sline_a,
 struct convert_config *pCSetup_a );

static void Lprintf
( const char *sString_a );

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  char sFileName[FILENAME_SIZE];
  SINT4 iSize;
  struct product_hdr PHeader;
#define BUFFER_SIZE (5120)
  UINT1 iBuffer[BUFFER_SIZE];
  char log_str[200];

  setlocale( LC_ALL, "" ) ;

  /* Process the command line arguments */
  ProcessCommandLine( iArgc, sArgv, sFileName );

  /* Process the setup file, if any */
  LoadSetup( &CConfig_c );

  /* Open the logging file, if any */
  if (CConfig_c.lLogToFile)
    {
      char *sptr;
      char sLogPath[PATHNAME_SIZE];
      sptr = getenv( "IRIS_LOG" );
      if (sptr)
	{
	  strncpy( sLogPath, sptr, PATHNAME_SIZE-20 );
	  sLogPath[PATHNAME_SIZE-20] = 0;
	}
      else
	strcpy( sLogPath, "/tmp/" );
      strcat( sLogPath, "ChProductName.log" );
      if (CConfig_c.lAppendToLog)
	CConfig_c.iLogFile = fopen( sLogPath, "a" );
      else
	CConfig_c.iLogFile = fopen( sLogPath, "w" );
      if (NULL == CConfig_c.iLogFile)
	{
	  fprintf( stderr, "Can not open log file: '%s'\n", sLogPath );
	  exit(EXIT_FAILURE);
	}
    }

  /* Starting here we can use the Lprintf function */
  if (CConfig_c.lVerbose)
    {
      sprintf(log_str, "Processing input file: %s\n", sFileName );
      Lprintf(log_str);
    }

  /* Process the product header */
  iSize = fread( &PHeader, sizeof(char), sizeof(struct product_hdr), stdin); 
  if (iSize == sizeof(struct product_hdr)) 
    {
      ChangeProductHeader( &PHeader );
      fwrite( &PHeader, 1, iSize, stdout);  
    }
  else
    {
      sprintf(log_str, "File size %d smaller than product header.\n", iSize );
      Lprintf(log_str);
      fwrite( &PHeader, 1, iSize, stdout);  
      exit( EXIT_FAILURE );
    }

  iSize = fread(iBuffer, sizeof(char), BUFFER_SIZE, stdin); 
  while( iSize > 0 )
    {
      fwrite(iBuffer, 1, iSize, stdout);  
      iSize = fread(iBuffer, sizeof(char), BUFFER_SIZE, stdin); 
    }

  exit( EXIT_SUCCESS );
}
/* ======================================
 * Change product name in the product header, if applicable.
 */
static void ChangeProductHeader
(struct product_hdr *phdr_a )
{
  SINT4 ii;
  for (ii=0; ii<CConfig_c.iPNameCount; ii++)
    {
      struct pname_table *pPName;
      char sFileSite16[20];
      nullify_ncpy( sFileSite16, phdr_a->end.sing_sitename, 16 );
      pPName = &(CConfig_c.Table[ii]);
      if (sig_fnmatch( pPName->sOldSitename16, sFileSite16 ))
	{
	  if ( (pPName->iOldPType==PTYPE_WILD)||
	       (pPName->iOldPType == phdr_a->pcf.iptype) )
	    {
	      char sFilePName[16];
	      nullify_ncpy( sFilePName, phdr_a->pcf.spname, 12 );
	      if (sig_fnmatch( pPName->sOldPName, sFilePName ))
		{
		  if (CConfig_c.lVerbose)
		    {
		      char log_str[100];
		      sprintf(log_str, "PName changed from '%12.12s' to '%12.12s'\n", 
			      phdr_a->pcf.spname, pPName->sNewPName );
		      Lprintf(log_str);
		    }
		  memcpy( phdr_a->pcf.spname, pPName->sNewPName, 12 );
		  break;
		}
	    }
	}
    }
}

/* ======================================================================
 * Function to process the command line arguments.
 */
static void ProcessCommandLine
(int iArgc_a, 
 char *sArgv_a[],
 char sFilename_a[FILENAME_SIZE] )
{

  int iArg;

  /* Parse command line options
   */
  for( iArg=1 ; iArg<iArgc_a ; iArg++ )
    {
      const char *sArg = sArgv_a[iArg];
      /* fprintf( stderr, "Processing %d: '%s'\n", iArg, sArg ); */

      if (sArg[0] == '-')
	{
	  if (0 == strncmp( sArg, "-h", 2 ))
	    {
	      Usage();
	    }
	}
      else
	strncpy( sFilename_a, sArg, FILENAME_SIZE-1 );
      
    }
  return;
}
/* ==================================================
 */
static void Usage( void )
{
  fprintf( stderr, "Options: -help\n" );
  fprintf( stderr, "         filename\n" );
  fprintf( stderr, "         (all other arguments ignored)\n" );
  exit(EXIT_FAILURE) ;
}

/* ============================================================
 * Function to load the convert setup from a configuration file.
 */
static void LoadSetup
( struct convert_config *pCSetup_a )
{
  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) );

  /* 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, 140 );
  strcat( sPath, "ChProductName.conf" );
  stream = fopen( sPath, "r" );

  /* There is no point in running without a config file.  This is
   * before we have the log file operational. */
  if (NULL == stream)
    {
      fprintf( stderr, "Sorry, missing ChProductName.conf file.\n" );
      exit(EXIT_FAILURE);
    }

  /* 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 ChProductName.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.
 *
 * All blank lines and
 * lines beginning with "#" are ignored.  Commands can be in any order.
 * Commands take effect in the order received.
 */
static UINT2 ProcessConfigLine
(char *sline_a,
 struct convert_config *pConfig_a )
{
  char *stokens[9];
  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;
    }
  
  /* 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;
    }

  /* The PNAME command is the important one.  It takes the following
   * arguments.  Note that if any strings have imbedded whitespace, they
   * need to be quoted with ".
   *  1) Input sitename      (any string including * and ? wildcards)
   *  2) Input product type  (any valid 5-char ptype, or "*" ) (else syntax error)
   *  3) Input product name  (any string including * and ? wildcards)
   *  4) Output product name
   */
  if ( 0 == strcmp( "PNAME", stokens[0] ) )
    {
      struct pname_table *pPName;
      if (icount<5) return TRUE;
      if (pConfig_a->iPNameCount>=PRODUCT_CHANGE_MAX) return TRUE;

      pPName = &pConfig_a->Table[pConfig_a->iPNameCount];
      strncpy( pPName->sOldSitename16, stokens[1], sizeof(pPName->sOldSitename16)-1);
      if (0 == strcmp( stokens[2], "*" ))
	pPName->iOldPType = PTYPE_WILD;
      else
	pPName->iOldPType = iprod_type(stokens[2]);
      strncpy( pPName->sOldPName, stokens[3], sizeof(pPName->sOldPName)-1 );
      spacify_ncpy( pPName->sNewPName, stokens[4], 12 );
      pConfig_a->iPNameCount++;
      return FALSE;
    }
  
  if ( 0 == strcmp( "VERBOSE", stokens[0] ) )
    {
      pConfig_a->lVerbose = TRUE;
      return FALSE;
    }

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

/* ===========================================
 * Print string to log and terminal, if desired.
 */
static void Lprintf
( const char *sString_a )
{
  if (CConfig_c.lLogToTerm) fputs( sString_a, stderr );
  if (CConfig_c.lLogToFile)
    {
      fputs( sString_a, CConfig_c.iLogFile );
      fflush( CConfig_c.iLogFile );
    }
}
