/* **************************************************************
 * *                                                            *
 * *  Function to convert a BMP Satellite image to IRIS format  *
 * *                                                            *
 * **************************************************************
 * File: utils/pipes_in/BMPSatToIris.C
 *
 *                  COPYRIGHT (c) 2000, 2001, 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 a BMP satellite 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 <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "sigtypes.h"
#include "projection.h"
#include "headers.h"
#include "output.h"
#include "product.h"
#include "dsp.h"
#include "dsp_lib.h"
#include "misc_lib.h"
#include "user_lib.h"

/* This holds information to customize the convertor */
struct sat_defs
{
  UINT1 lLogToFile;
  UINT1 lLogToTerm;
  UINT1 lVerbose;
  UINT1 lAppendToLog;
  FILE *iLogFile;
  SINT4 iWidth;          /* Size in pixels */
  SINT4 iHeight;
  FLT8  fPixelWidthKm;   /* Size of pixel in km */
  FLT8  fPixelHeightKm;
  struct latlon8 Center;
  char  sSitename[16];   /* space padded */
  UINT1 lRecordUtc;      /* Record using UTC (else local) */
  SINT2 iLocalWest;      /* Time offset west to local TZ */
  char  sLocalTZName[TZNAME_SIZE]; /* Name of local TZ */
};

static struct sat_defs CConfig_c;
static char log_str[200];

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

static void Usage( void );

static void ConvertBMPSatToIris
( const char *sFileName_a,
  const struct sat_defs *pDef_a );

static UINT1 FillProductHeader
(struct product_hdr *pPhead_a, 
 const char *sFile_a,            /* Just filename */
 const struct sat_defs *pDefs_a );

static UINT1 GetTimeFromFilename
( struct full_time_spec *pTime_a,
  const char *sFile_a,
  const struct sat_defs *pDefs_a );

static void ProcessConfigFile
( struct sat_defs *pDefs_a );

static UINT1 ProcessConfigLine
(char *sLine_a,
 struct sat_defs *pDefs_a );

static void Lprintf
( const char *sString_a );

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  char sFileName[FILENAME_SIZE];

  setlocale( LC_ALL, "" ) ;

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

  /* Process the setup file, if any */
  ProcessConfigFile( &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, "BMPSatToIris.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 */

  ConvertBMPSatToIris( sFileName, &CConfig_c );

  exit( EXIT_SUCCESS );
}

/* ======================================================================
 * 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) ;
}

/* ===========================================
 * 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 );
    }
}


/* =======================================================
 * Process the config file.
 */
static void ProcessConfigFile
( struct sat_defs *pDefs_a )
{
  char sPath[PATHNAME_SIZE];
  char sline[81], *sptr;
  FILE* stream;
  SINT4 iline;
  UINT2 lerror;

  /* Initialize the data to the default values */
  memset( pDefs_a, 0, sizeof(*pDefs_a) );

  pDefs_a->iWidth = 1000;
  pDefs_a->iHeight = 1000;
  pDefs_a->fPixelWidthKm = 1.0;
  pDefs_a->fPixelHeightKm = 1.0;
  pDefs_a->Center.fLon = (FPI/180.0) * 114.9400;
  pDefs_a->Center.fLat = (FPI/180.0) *   4.9525;
  memcpy( pDefs_a->sSitename, "BRUNEI, radar       ", 16);
  pDefs_a->lRecordUtc = FALSE;
  pDefs_a->iLocalWest = -8*60;  /* TZ in Brunei */
  strcat( pDefs_a->sLocalTZName, "BNT" );

  /* 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, "BMPSatToIris.conf" );
  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, pDefs_a );
      if (lerror) fprintf( stderr, "Syntax error on BMPSatToIris.conf file line %d\n", iline );
      iline +=1;
      sptr = fgets( sline, 81, stream );
    }

  fclose( stream );


  /* Check for required configuration */
  if (!pDefs_a->sSitename[0])
    {
      fprintf( stderr, "Sorry, SITE line required in config file\n" );
      exit(EXIT_FAILURE);
    }
}
/* ====================================================================
 * Process one line of the config file.  Return true if there is a syntax
 * error.
 */
static UINT1 ProcessConfigLine
(char *sLine_a,
 struct sat_defs *pDefs_a )
{
  UINT4 icount, ioffset;
  SINT4 iTemp;
  FLT8 fTemp;
  char *stokens[5], *sptr;

  /* Process up to 5 tokens, stop when the line is exhausted */
  ioffset = 0;
  icount = 0;
  while( icount<5)
    {
      stokens[icount] = sigtok( sLine_a, &ioffset );
      if (NULL == stokens[icount])
	break;
      else
	icount++;
    }

  /* Blank lines are allowed */
  if (icount==0) return FALSE;

  /* Skip comment lines */
  if (stokens[0][0] == '#') return FALSE;
  
  if ( 0 == strcmp( "APPEND_TO_LOG", stokens[0] ) )
    {
      pDefs_a->lAppendToLog=TRUE;
      return FALSE;
    }

  if (0 == strcmp( "CENTER_LAT", stokens[0]) )
    {
      if (icount<2) return TRUE; 
      fTemp = strtod( stokens[1], &sptr );
      if (sptr == stokens[1]) return TRUE;
      if (fTemp>90.0) return TRUE;
      if (fTemp<-90.0) return TRUE;
      pDefs_a->Center.fLat = (FPI/180.0) * fTemp;
      return FALSE;
    }

  if (0 == strcmp( "CENTER_LON", stokens[0]) )
    {
      if (icount<2) return TRUE; 
      fTemp = strtod( stokens[1], &sptr );
      if (sptr == stokens[1]) return TRUE;
      if (fTemp> 180.0) return TRUE;
      if (fTemp<-180.0) return TRUE;
      pDefs_a->Center.fLon = (FPI/180.0) * fTemp;
      return FALSE;
    }

  if (0 == strcmp( "LOCAL_TIMEZONE", stokens[0]) )
    {
      if (icount<3) return TRUE; 
      strncpy( pDefs_a->sLocalTZName, stokens[1], TZNAME_SIZE-1 );
      pDefs_a->sLocalTZName[TZNAME_SIZE-1] = 0;
      pDefs_a->iLocalWest = strtol( stokens[2], &sptr, 10 );
      if (sptr == stokens[2]) return TRUE;
      if (pDefs_a->iLocalWest >  18*60) return TRUE;
      if (pDefs_a->iLocalWest < -18*60) return TRUE;
      return FALSE;
    }

  if (0 == strcmp( "PIXEL_X_RES", stokens[0]) )
    {
      if (icount<2) return TRUE; 
      fTemp = strtod( stokens[1], &sptr );
      if (sptr == stokens[1]) return TRUE;
      if (fTemp>10.0) return TRUE;
      if (fTemp<0.0) return TRUE;
      pDefs_a->fPixelWidthKm = fTemp;
      return FALSE;
    }

  if (0 == strcmp( "PIXEL_Y_RES", stokens[0]) )
    {
      if (icount<2) return TRUE; 
      fTemp = strtod( stokens[1], &sptr );
      if (sptr == stokens[1]) return TRUE;
      if (fTemp>10.0) return TRUE;
      if (fTemp<0.0) return TRUE;
      pDefs_a->fPixelHeightKm = fTemp;
      return FALSE;
    }

  if (0 == strcmp( "PIXEL_X_COUNT", stokens[0]) )
    {
      if (icount<2) return TRUE; 
      iTemp = atoi( stokens[1] );
      if (!stokens[1]) return TRUE;
      if (iTemp>1100) return TRUE;
      if (iTemp<0) return TRUE;
      pDefs_a->iWidth = iTemp;
      return FALSE;
    }

  if (0 == strcmp( "PIXEL_Y_COUNT", stokens[0]) )
    {
      if (icount<2) return TRUE; 
      iTemp = atoi( stokens[1] );
      if (!stokens[1]) return TRUE;
      if (iTemp>1100) return TRUE;
      if (iTemp<0) return TRUE;
      pDefs_a->iHeight = iTemp;
      return FALSE;
    }

  /* Check for RECORDED_TIME command */
  if ( 0 == strcmp( "RECORDED_TIME", stokens[0] ) )
    {
      if ( icount < 2 ) return TRUE;
      
      if      ( 0 == strcmp( "LOCAL", stokens[1] ) ) 
	pDefs_a->lRecordUtc = FALSE;
      else if ( 0 == strcmp( "UTC", stokens[1] ) ) 
	pDefs_a->lRecordUtc = TRUE;
      else
	return TRUE;
      return FALSE;
    }

  /* Check for LOG command */
  if ( 0 == strcmp( "LOG", stokens[0] ) )
    {
      if ( icount < 2 ) return TRUE;
      
      if      ( 0 == strcmp( "BOTH", stokens[1] ) ) 
	{pDefs_a->lLogToFile=TRUE;  pDefs_a->lLogToTerm=TRUE;}
      else if ( 0 == strcmp( "FILE", stokens[1] ) ) 
	{pDefs_a->lLogToFile=TRUE;  pDefs_a->lLogToTerm=FALSE;}
      else if ( 0 == strcmp( "TERM", stokens[1] ) )
	{pDefs_a->lLogToFile=FALSE; pDefs_a->lLogToTerm=TRUE;}
      else if ( 0 == strcmp( "NONE", stokens[1] ) )
	{pDefs_a->lLogToFile=FALSE; pDefs_a->lLogToTerm=FALSE;}
      else
	return TRUE;
      return FALSE;
    }

  /* Check for SITE command */
  if (0 == strcmp( "SITE", stokens[0]) )
    {
      char sSitenameNull[20];
      if (icount<2) return TRUE;
      strncpy( sSitenameNull, stokens[1], 20-1 );
      sSitenameNull[20-1] = 0;
      spacify_ncpy( pDefs_a->sSitename, sSitenameNull, 16 );
      return FALSE;
    }

  /* Unrecognized lines will generate a syntax error */
  return TRUE;
}
/* ==================================
 * Process one input file.
 */
static void ConvertBMPSatToIris
(const char *sFile_a,            /* Just filename */
 const struct sat_defs *pDefs_a )
{
  struct product_hdr phdr;
  UINT1  lError;
  SINT4  iY, iDataSize;
  UINT1  *pData;
  char log_str[200];
 
  sleep(5);
  sprintf(log_str, "Processing input file: %s\n", sFile_a);
  Lprintf(log_str);
  
  lError = FillProductHeader( &phdr, sFile_a, pDefs_a );
  if (lError) return;  /* lError indicates that an error was already reported */

  iDataSize = phdr.pcf.ixsize * phdr.pcf.iysize;
  pData = (UINT1 *)malloc( iDataSize );
  if (!pData)
    {
      Lprintf("Unable to malloc the buffer\n" );
      return;
    }

  fread(&pData[0], 1, 1078, stdin );

  /* fopen succeeded */
  for (iY=0; iY<phdr.pcf.iysize; iY++)
    {
      /* SINT4 iX;
       *      for (iX=0; iX<phdr.pcf.ixsize; iX++)
       */
	{
	  int iOffset, iSize;
	  
	  iOffset = ((phdr.pcf.iysize - iY) - 1)*phdr.pcf.ixsize;
	  iSize = fread(&pData[iOffset], 1, phdr.pcf.ixsize, stdin );

	  if (iSize != phdr.pcf.ixsize)
	    {
	      sprintf(log_str, "File read size line %d wrong, got %d, expected %d\n",
		      iY, iSize, phdr.pcf.ixsize );
	      Lprintf(log_str);
	      exit(EXIT_FAILURE);
	    }
	}
    }
	  
  /****
       If we have a good area file, then we must now convert it into an IRIS
       user product.  So we take the steps to do that.
  ****/
  
  sprintf(log_str, "Processing satellite file: %s\n", sFile_a);
  Lprintf(log_str);
  
  /****
       Now we write the data of the user file to disk.
  ****/
  
  fwrite(&phdr, sizeof(struct product_hdr), 1, stdout);
  
  for(iY=phdr.pcf.iysize; iY>0; iY--)
    {
      fwrite(&pData[iY*phdr.pcf.ixsize], 1, phdr.pcf.ixsize, stdout);  
    }
  
  free(pData);
  
}

/* ========================================================
 * Fill in the product header.  Returns non-zero on error.
 */
static UINT1 FillProductHeader
(struct product_hdr *pPhead_a, 
 const char *sFile_a,
 const struct sat_defs *pDefs_a )
{
  UINT1  lError;
  struct full_time_spec Time;

  if (pDefs_a->iWidth > MAX_PRODUCT_SIZE || pDefs_a->iHeight > MAX_PRODUCT_SIZE)
    {
      sprintf(log_str, "Error - input dimensions too big. (max is %d) Width=%d  Height=%d\n",
	      MAX_PRODUCT_SIZE, pDefs_a->iWidth, pDefs_a->iHeight);
      Lprintf(log_str);
      return TRUE;
    }

  lError = GetTimeFromFilename( &Time, sFile_a, pDefs_a );
  if (lError) return lError;

  HeaderFromScratch( pPhead_a, &Time, pDefs_a->sSitename, pDefs_a->sSitename, "Satellite" );

  pPhead_a->pcf.hdr.ibytes = sizeof (struct product_configuration);
  pPhead_a->pcf.iptype = PROD_USER;
  
  pPhead_a->pcf.ixscale = 
    NINT((pDefs_a->fPixelWidthKm) * 1000 * 100);
  pPhead_a->pcf.iyscale = 
    NINT((pDefs_a->fPixelHeightKm) * 1000 * 100);
  pPhead_a->pcf.izscale = 0;
  pPhead_a->pcf.ixsize = pDefs_a->iWidth;
  pPhead_a->pcf.iysize = pDefs_a->iHeight;
  pPhead_a->pcf.izsize = 1;
  pPhead_a->hdr.ibytes = sizeof (struct product_hdr) + (pPhead_a->pcf.ixsize * pPhead_a->pcf.iysize);
  pPhead_a->pcf.idata_out = DB_USER;
  pPhead_a->pcf.idata_in  = DB_USER;
  pPhead_a->pcf.iprojection_type = MAPROJ_MERCATOR;
  pPhead_a->pcf.colors.iflags = (COLOR_SCALE_TOP_SAT | COLOR_SCALE_BOT_SAT);
  pPhead_a->pcf.colors.istart = 300;
  pPhead_a->pcf.colors.istep  = 200;
  pPhead_a->pcf.colors.icolcnt = 12;
  pPhead_a->pcf.colors.iset_and_scale = 0x0202;
  
  pPhead_a->end.ilon_center = iBin4FromRadian(pDefs_a->Center.fLon);
  pPhead_a->end.ilat_center = iBin4FromRadian(pDefs_a->Center.fLat);
  pPhead_a->end.ilon_ref = iBin4FromRadian(pDefs_a->Center.fLon);
  pPhead_a->end.ilat_ref = iBin4FromRadian(pDefs_a->Center.fLat);
  
  pPhead_a->end.ignd_hgt = 50;
  pPhead_a->end.irad_hgt = 10;

  return FALSE;
  
}

/* ====================================
 * Parses the filename to determine the date and time.
 */
static UINT1 GetTimeFromFilename
( struct full_time_spec *pTime_a,
  const char *sFile_a,
  const struct sat_defs *pDefs_a )
{
  int iYear, iMonth, iDay, iHour, iMinute;
  char s1[10];
  /* Filename consists of GMSyyyymmddhhmm.BMP in UTC */
  sscanf( sFile_a, "%3s%4d%2d%2d%2d%2d", s1, &iYear, &iMonth, 
	  &iDay, &iHour, &iMinute);
  /* fprintf( stderr, "Time: %4d %2d %2d: %2d %2d\n", iYear, iMonth, iDay, iHour, iMinute ); */

  memset( pTime_a, 0, sizeof(*pTime_a) );
  pTime_a->Ymds.iyear = iYear;
  pTime_a->Ymds.imon  = iMonth;
  pTime_a->Ymds.iday  = iDay;
  pTime_a->Ymds.isec  = iPackTime( iHour, iMinute, 0 );

  strcpy( pTime_a->sLocalTZName, pDefs_a->sLocalTZName );
  pTime_a->iLocalWest = pDefs_a->iLocalWest;
  if (pDefs_a->lRecordUtc)
    {
      pTime_a->Ymds.imills |= YMDS_FLG_UTC;
      strcpy( pTime_a->sRecordTZName, "UTC" );
      pTime_a->iMinutesWest = 0;
    }
  else
    {
      ymds_add_seconds( -60*pDefs_a->iLocalWest, &(pTime_a->Ymds) );
      strcpy( pTime_a->sRecordTZName, pDefs_a->sLocalTZName );
      pTime_a->iMinutesWest = pDefs_a->iLocalWest;
    }
  return FALSE;
}
