/* **********************************************************************
 * *                                                                    *
 * *      Interface to Read Satellite images into IRIS                  *
 * *           Only for use on the SeaLaunch system                     *
 * **********************************************************************
 * File: utils/pipes_in/McIdasSatToIris.C
 *
 *                        COPYRIGHT (c) 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.
 */

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

#include "sigtypes.h"
#include "headers.h"
#include "product.h"
#include "user_lib.h"
#include "misc_lib.h"

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

struct convert_config CConfig_c;

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

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

static void Usage( void );

static void LoadSetup
( struct convert_config *pCSetup_a );
static void ProcessFile( void );

static void GetTimeFromDirBlock
(struct full_time_spec *pDataTime_a,
 const int dir_block_a[64] );

static void Lprintf(const char *log_str);

/*
  Here are some limitation placed upon the input file:
  1) The file must be Mercator projection
  2) The file can have some blank data on the bottom and on the right.
     It has been observed that this is an outcome of converting the
     normal satellite AREA file to a mercator projection.
  3) The data area (after the blanks are stripped) must not exceed
     1100 pixels in either dimention.
*/

int main (int iArgc, char *sArgv[])
{
  setlocale( LC_ALL, "" ) ;

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

  /* 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, "McIdasSatToIris.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);
    }
  } 
  ProcessFile();
  exit(0);
}

/* ==================================
 * Process one input file.
 */
static void ProcessFile(void)
{
  int    i, dir_block[64], nav_block[8];
  int    row, col, dat_block_size;
  char   dat_block[2000000], tmp_block[10000];
  char   tmp_str[200];
  struct product_hdr phdr;
  float  tmp_lon, tmp_lat, pix_per_deg, lat_pix_ratio;
  int    lon_pix_dif, lat_pix_dif;
  struct full_time_spec DataTime;

/* fopen succeeded */
  /****
       The satellite file is structured as a directory block, then a navigation
       block, then a calibration block, then a supplemental block, then a data
       block.  The dir block and nav block has stuff in them that give us
       the charactoristics of the satellite data.  So read them in first.  Then
       we read in, but trash the cal block and the supp block.  Then based on
       what the dir block and nav block tell us, we read in the data block.
  ****/
      
  fread(dir_block, sizeof(int), 64, stdin);
  fread(nav_block, sizeof(int),  8, stdin);
  
  if (dir_block[33] > 10000)
  {
    Lprintf("Error in area file - dir / nav / cal / supp blocks too large.\n");
    return;
  }
  
  row = dir_block[8];
  col = dir_block[9];
  dat_block_size = row * col;
      
  if (row > 1100 || col > 1100)
  {
    sprintf(tmp_str, "Error - input dimensions too big. (max is 1100) row=%d  col=%d\n",
	      row, col);
    Lprintf(tmp_str);
    return;
  }
      
  /**** 
	Skip over the cal block and supp block and read in the data block.  
	Note that dat block words 8 and 9 are the x and y number of pixels in file.
  ****/
  fread(tmp_block, sizeof(char), dir_block[33]-(72*4), stdin);
  fread(dat_block, sizeof(char), dat_block_size, stdin); 
	  
  /****
       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.
  ****/
  
  Lprintf("Processing satellite file.\n");
  
  GetTimeFromDirBlock( &DataTime, dir_block );

  HeaderFromScratch( &phdr, &DataTime, "SL, wxradar1    ", "SL, wxradar1    ",
		     "Satellite" ); 

  phdr.pcf.iptype = PROD_USER;

  /****
       The pixel scale is determined by the row and column resolution in the
       dir block times the resolution in the nav block.
  ****/
  phdr.pcf.ixscale = dir_block[12] * nav_block[4] * 100;
  phdr.pcf.iyscale = dir_block[11] * nav_block[4] * 100;
  phdr.pcf.izscale = 0;
  phdr.pcf.ixsize = col;
  phdr.pcf.iysize = row;
  phdr.pcf.izsize = 0;
  phdr.pcf.ixrad = 0;
  phdr.pcf.iyrad = 0;
  phdr.pcf.izrad = 0;
  phdr.pcf.irange_last_v20 = 0;
  phdr.pcf.idata_out = DB_USER;
  phdr.pcf.idata_in  = DB_USER;
  phdr.pcf.iprojection_type = 1;
  phdr.pcf.colors.iflags = (COLOR_SCALE_TOP_SAT | COLOR_SCALE_BOT_SAT);
  phdr.pcf.colors.istart = 500;
  phdr.pcf.colors.istep  = 100;
  phdr.pcf.colors.icolcnt = 16;
  phdr.pcf.colors.iset_and_scale = 0x0101;
  
  /****
       The lat and lon come from the info in the nav block.  Convert these
       to IRIS format
  ****/
  
  /****
       The difference from little image center to center long point is: 
       center long point - (horz offset into big picture + (little image pixels * little image res / 2))
       \_________ center of little image __________/
  ****/
  
  lon_pix_dif = nav_block[2] - (dir_block[6] + (dir_block[9] * dir_block[12] / 2));
  
  /* pix_per_deg: (circumference of earth in km / pixels per km) / degrees in earth */ 
  pix_per_deg = ( ( ((float)nav_block[6] / 1000.0) * 2.0 * FPI_P) / ((float)nav_block[4] / 1000.0) ) / 360.0;
  
  tmp_lon = (float)(nav_block[5] / 10000) +
    (float)((float)((nav_block[5] % 10000) / 100) / 60.0) +
    (float)((float)(nav_block[5] % 100) / 3600.0);
  
  /* lon = center lon in degrees + (lon_pix_dif / pixels per degree) */
  tmp_lon = tmp_lon + ((float)lon_pix_dif / pix_per_deg);
  
  /* now scale lon for IRIS */
  tmp_lon = 360.0 - tmp_lon;
  tmp_lon = tmp_lon / 360.0;
  tmp_lon = tmp_lon * 0xFFFFFFFF;
  tmp_lon += 0.5;
  phdr.end.ilon_center = (BIN4)tmp_lon; 
  
  /* lat_pix_dif: center lat point - (vert offset into big picture + (little image pixels * little image res / 2)) */
  lat_pix_dif = nav_block[1] - (dir_block[5] + (dir_block[8] * dir_block[11] / 2));
  
  /* lat_pix_ratio: pixel offset from equator to vert cent of little image in pixels / radius of earth in km*/
  lat_pix_ratio = fabs((float)lat_pix_dif / ((float)nav_block[6] / 1000.0));
  
  /* given the pixel ratio to the center of the little image, find its latitude */
  tmp_lat = 2.0 * (atan(exp(lat_pix_ratio)) - (FPI_P/4.0));
  tmp_lat = tmp_lat * 180.0 / FPI_P;
  
  /* we are south latitude when lat_pix_dif is less than 0) */
  if (lat_pix_dif < 0.0) tmp_lat = 360.0 - tmp_lat;
  tmp_lat = tmp_lat / 360.0;
  tmp_lat = tmp_lat * 0xFFFFFFFF;
  tmp_lat += 0.5;
  phdr.end.ilat_center = (BIN4)tmp_lat;
  
  
  /****
       Now we write the data of the user file to disk.
  ****/
  fwrite(&phdr, sizeof(struct product_hdr), 1, stdout);
  
  for(i=row-1; i>=0; i--)
  {
    fwrite(&dat_block[i*col], 1, col, stdout);  
  }
}

/* ======================================================
 * Get the time information.
 */
static void GetTimeFromDirBlock
(struct full_time_spec *pDataTime_a,
 const int dir_block_a[64] )
{
  int    imon, imday, buf_yday, break_flag;
  struct tm in_time, out_time;

  buf_yday = dir_block_a[3] % 1000 - 1;
  in_time.tm_year = dir_block_a[3] / 1000;
  in_time.tm_sec = dir_block_a[4] % 100;
  in_time.tm_min = (dir_block_a[4] % 10000) / 100;
  in_time.tm_hour = dir_block_a[4] / 10000;
  
  /****
       We know the julian day of the year (buf_yday), but we must
       convert this into a month and day.  This algorithm does that.
  ****/
  break_flag = 0;
  for (imon = 0; imon < 12; imon++)
  {
    in_time.tm_mon = imon;
    for (imday = 1; imday < 32; imday++)
    {
      time_t tmp_time;
      in_time.tm_mday = imday;
      tmp_time = mktime(&in_time);
      gmtime_r(&tmp_time, &out_time );
      if (out_time.tm_yday == buf_yday)
      {
        break_flag = 1;
        break;
      }
    }
    if (break_flag) break;
  }
  
  /****
       We must come up with a date and time for the internal structures.  
       The date and time come from the 
       dir block of the area file
  ****/
  if (out_time.tm_year >= 100) out_time.tm_year -= 100;

  memset( pDataTime_a, 0, sizeof(*pDataTime_a) );

  pDataTime_a->Ymds.iyear = in_time.tm_year + 1900;
  pDataTime_a->Ymds.imon  = out_time.tm_mon + 1;
  pDataTime_a->Ymds.iday  = out_time.tm_mday;
  pDataTime_a->Ymds.isec  = (in_time.tm_hour * 3600) + (in_time.tm_min * 60) + 
    in_time.tm_sec;
  /* Recorded UTC */
  pDataTime_a->Ymds.imills |= YMDS_FLG_UTC; 
  pDataTime_a->iMinutesWest = 0;

  /* No local time knowledge, so assume UTC */
  pDataTime_a->iLocalWest = 0;
  strcpy( pDataTime_a->sLocalTZName, "UTC" );
  return;
} 

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

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

  fclose( stream );

  return;
}

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

  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();
      }
    }
  }
  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>
 * SITE name16 name3 lat long
 * VERBOSE
 *
 *
 */
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;
  }

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

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