/* **************************************
 * *                                    *
 * *  Archive 2 format viewer program   *
 * *                                    *
 * **************************************
 * File: iris/examiners/archive2view.C
 *
 *                      COPYRIGHT (c) 2002, 2004  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 take the filename of an archive2 file and prints out diagnostic
 * information.
 */

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

#include "sigtypes.h"

#include "nexrad.h"

#include "user_lib.h"

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

static void Usage( void );

static void PrintTitle
( struct nr_title *pTitle_a );  /* Strings are byte swapped correctly */

static void PrintMessageHeader
( struct nr_message_header *pMHeader_a,
  const struct view_options *pOptions_a );

static void PrintDataHeader
( struct nr_radar_data_header *pRHeader_a,  /* Strings are byte swapped correctly */
  const struct view_options *pOptions_a );

static void PrintData
( const struct nr_packet *pRecord_a,  
  const struct view_options *pOptions_a );

static char *sTailString
(const char *sString_a,
 SINT4 iMaxSize_a );

static void PrintOneData
( const UINT1 iData_a[],
  SINT2 iBinCount_a,
  FLT4  fGain_a,
  FLT4  fOffset_a,
  SINT4 iLineLength_a );

static void Pack2( UINT2 *pData2_a );

static void Pack4( UINT4 *pData4_a );

static void YmdsFromNrTime
( struct ymds_time *pYmds_a,
  UINT4 iDate_a,    /* Date in days */
  UINT4 iTime_a );  /* Time in ms */

struct view_options
{
  char sPath[PATHNAME_SIZE];
  UINT1 lVerbose;      /* Print full header info */
  UINT1 lPrintData;    /* Print full data info */
  SINT4 iRayCount;     /* Only print this many rays */
  SINT4 iRaySkip;      /* Skip this many rays */
  SINT4 iLineLength;   /* Line length to use for data listing */
};

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  SINT4 iCount, iReadRayCount, iPrintRayCount;
  struct view_options Options;
  struct nr_title Title;
  struct nr_packet Packet;
  FILE *stream;

  setlocale( LC_ALL, "" ) ;

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

  /* Open the file */
  stream = fopen( Options.sPath, "r" );
  if (!stream)
    {
      perror( "fopen" );
      exit(EXIT_FAILURE);
    }

  /* Read and display the Title */
  iCount = fread( &Title, 1, sizeof(Title), stream );
  if (iCount != sizeof(Title)) 
    {
      printf( "Bad read, got%d, wanted%d bytes\n", iCount, sizeof(Title));
    }
  PrintTitle( &Title );

  /* Loop reading in records, and displaying the results */
  iReadRayCount = 0;
  iPrintRayCount = 0;
  iCount = fread( &Packet, 1, sizeof(Packet), stream );
  while( iCount==sizeof(Packet) )
    {
      iReadRayCount++;
      if (iReadRayCount<=Options.iRaySkip) goto SKIP_THIS_RAY;
      iPrintRayCount++;
      if ((Options.iRayCount) &&(iPrintRayCount>Options.iRayCount)) goto DONE;

      /* Print the record contents */
      PrintMessageHeader(&Packet.MessageHeader, &Options );
      PrintDataHeader(&Packet.DataHeader, &Options );
      PrintData(&Packet, &Options );

    SKIP_THIS_RAY:
      iCount = fread( &Packet, 1, sizeof(Packet), stream );
    }
  if (iCount !=0) 
    {
      printf( "Bad read, got%d, wanted%d bytes\n", iCount, sizeof(Packet));
    }
  else
    {
      if (ferror(stream)) printf( "Read error.\n" );
    }

 DONE:

  fclose( stream );

  exit( EXIT_SUCCESS );
}
/* ======================================================================
 * Function to process the command line arguments.
 */
static void ProcessCommandLine
(int iArgc_a, 
 char *sArgv_a[],
 struct view_options *pOptions_a )
{

  int iArg;
  memset( pOptions_a, 0, sizeof(*pOptions_a) );
  pOptions_a->iLineLength = 80;

  /* 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, "-count:", 7 ))
	    {
	      char *sEnd;
	      pOptions_a->iRayCount = strtol( &sArg[7], &sEnd, 10 );
	    }
	  else if (0 == strncmp( sArg, "-d", 2 ))
	    {
	      pOptions_a->lPrintData=TRUE;
	    }
	  else if (0 == strncmp( sArg, "-h", 2 ))
	    {
	      Usage();
	      exit(EXIT_FAILURE) ;
	    }
	  else if (0 == strncmp( sArg, "-length:", 8 ))
	    {
	      char *sEnd;
	      pOptions_a->iLineLength = strtol( &sArg[8], &sEnd, 10 );
	      if (pOptions_a->iLineLength<30) pOptions_a->iLineLength=30;
	    }
	  else if (0 == strncmp( sArg, "-skip:", 6 ))
	    {
	      char *sEnd;
	      pOptions_a->iRaySkip = strtol( &sArg[6], &sEnd, 10 );
	    }
	  else if (0 == strncmp( sArg, "-v", 2 ))
	    {
	      pOptions_a->lVerbose=TRUE;
	    }
	}
      else
	{
	  strncpy( pOptions_a->sPath, sArg, PATHNAME_SIZE-1);
	}
    }
  if (!pOptions_a->sPath[0])
    {
      printf( "archive2view requires a pathname arg.\n" );
      exit(EXIT_FAILURE);
    }
  return;
}
/* ==================================================
 */
static void Usage( void )
{
  fprintf( stderr, "Options: -count:N  (only print N rays)\n" );
  fprintf( stderr, "         -data     (print data values)\n" );
  fprintf( stderr, "         -help     (print this list and exit)\n" );
  fprintf( stderr, "         -length:N (max line length to use)\n" );
  fprintf( stderr, "         -skip:N   (skip the first N rays)\n" );
  fprintf( stderr, "         -verbose  (print full header info)\n" );
  fprintf( stderr, "         pathname\n" );
  fprintf( stderr, "         (all other arguments ignored)\n" );
}

/* =====================================================
 * Unpack, then print the Title structure
 */
static void PrintTitle
( struct nr_title *pTitle_a )  /* Strings are byte swapped correctly */
{
  struct ymds_time Ymds;
  char sBuffer[21];

  /* Unpack the structure */
  Pack4( &pTitle_a->iFileDate );
  Pack4( &pTitle_a->iFileTime );

  printf( "====================== Title ======================\n" );
  printf( "Filename: '%-9.9s'", sTailString(pTitle_a->sFileName,9) );
  printf( ", Ext: '%-3.3s'\n", sTailString(pTitle_a->sFileExt,3) );
  YmdsFromNrTime( &Ymds, pTitle_a->iFileDate, pTitle_a->iFileTime );
  printf( "Date:%6d, Time:%6d (%s)\n", pTitle_a->iFileDate, pTitle_a->iFileTime,
	  shhmmssddmonyyyy_r(&Ymds, sBuffer) );

  return;

}
/* =====================================================
 * Unpack then print the message header
 */
static void PrintMessageHeader
( struct nr_message_header *pMHeader_a,  /* Strings are byte swapped correctly */
  const struct view_options *pOptions_a )
{
  struct ymds_time Ymds;
  char sTimeBuffer[21];
  
  /* Unpack the structure, including fields unused by IRIS, but not
   * other unused fields.
   */
  Pack2( &pMHeader_a->CTM[0] );
  Pack2( &pMHeader_a->CTM[1] );
  Pack2( &pMHeader_a->CTM[2] );
  Pack2( &pMHeader_a->CTM[3] );
  Pack2( &pMHeader_a->CTM[4] );
  Pack2( &pMHeader_a->CTM[5] );
  Pack2( &pMHeader_a->iMsgSize );
  Pack2( &pMHeader_a->iMsgType );
  Pack2( &pMHeader_a->iIDSequence );
  Pack2( &pMHeader_a->iMsgDate );
  Pack4( &pMHeader_a->iMsgTime );
  Pack2( &pMHeader_a->iSegmentCount );
  Pack2( &pMHeader_a->iSegmentNumber );

  if (!pOptions_a->lVerbose) return;

  printf( "\n================= Message Header ==================\n" );

  printf( " HW 1& 2: %-6d (CTM)          %-6d (CTM)\n", 
	  pMHeader_a->CTM[0], pMHeader_a->CTM[1] );
  printf( " HW 3& 4: %-6d (CTM)          %-6d (CTM)\n", 
	  pMHeader_a->CTM[2], pMHeader_a->CTM[3] );
  printf( " HW 5& 6: %-6d (CTM)          %-6d (CTM)\n", 
	  pMHeader_a->CTM[4], pMHeader_a->CTM[5] );
  printf( " HW 7& 8: %-6d (size)         %-6d (type)\n", 
	  pMHeader_a->iMsgSize, pMHeader_a->iMsgType );
  /* Separate time conversions, so there is no crosstalk/overflow */
  YmdsFromNrTime( &Ymds, pMHeader_a->iMsgDate, 0 );
  printf( " HW 9&10: %-6d (seq)          %-6d (%s)\n", 
	  pMHeader_a->iIDSequence, pMHeader_a->iMsgDate, 
	  sddmonyyyy_r(Ymds.iyear,Ymds.imon, Ymds.iday, sTimeBuffer) );
  Ymds.isec = pMHeader_a->iMsgTime/1000;
  Ymds.imills = MILLS_FROM_MS_FLAGS(pMHeader_a->iMsgTime%1000, YMDS_FLG_UTC);
  printf( " HW11&12: %-12d (%s.%03d)\n", 
	  pMHeader_a->iMsgTime, shhmmss_r(Ymds.isec, sTimeBuffer),
	  MS_FROM_MILLS(Ymds.imills) );
  printf( " HW13&14: %-6d (count)        %-6d (number)\n", 
	  pMHeader_a->iSegmentCount, pMHeader_a->iSegmentNumber );
  return;

}
/* =====================================================
 * Print the data header
 */
static void PrintDataHeader
( struct nr_radar_data_header *pDHeader_a,  /* Strings are byte swapped correctly */
  const struct view_options *pOptions_a )
{
  struct ymds_time Ymds;
  char sText[30], sTimeBuffer[21];
  FLT4 fAngle;

  /* Unpack the structure, including fields unused by IRIS, but not
   * other unused fields.
   */
  Pack4( &pDHeader_a->iDataTime );
  Pack2( &pDHeader_a->iDataDate );
  Pack2( &pDHeader_a->iUnambRange );
  Pack2( &pDHeader_a->iAzimuth );
  Pack2( &pDHeader_a->iRayNumber );
  Pack2( &pDHeader_a->iRayStatus );
  Pack2( &pDHeader_a->iElevation );
  Pack2( &pDHeader_a->iSweepNumber );
  Pack2( (UINT2*)&pDHeader_a->iBinStartRef );
  Pack2( (UINT2*)&pDHeader_a->iBinStartDop );
  Pack2( &pDHeader_a->iBinStepRef );
  Pack2( &pDHeader_a->iBinStepDop );
  Pack2( &pDHeader_a->iBinNumberRef );
  Pack2( &pDHeader_a->iBinNumberDop );
  Pack2( &pDHeader_a->iSectorNumber );
  Pack4( (UINT4*)&pDHeader_a->fSystemCal );  /* May need special float function */
  Pack2( &pDHeader_a->iRefPointer );
  Pack2( &pDHeader_a->iVelPointer );
  Pack2( &pDHeader_a->iWidPointer );
  Pack2( &pDHeader_a->iVelocityRes );
  Pack2( &pDHeader_a->iVolumePattern );
  Pack2( &pDHeader_a->ref_ptrp );
  Pack2( &pDHeader_a->vel_ptrp );
  Pack2( &pDHeader_a->spc_ptrp );
  Pack2( &pDHeader_a->iNyquistVel );
  Pack2( (UINT2*)&pDHeader_a->iAttenuation );
  Pack2( &pDHeader_a->min_dif );

  if (!pOptions_a->lVerbose)
    {
      fAngle = fPDegFromBin2(pDHeader_a->iAzimuth);
      printf( " Sweep:%3d, Ray:%4d, Az:%6.2f",
	      pDHeader_a->iSweepNumber, pDHeader_a->iRayNumber, fAngle );
      Ymds.isec = pDHeader_a->iDataTime/1000;
      fAngle = fElDegFromBin2(pDHeader_a->iElevation);
      printf( ", El:%5.2f, %s\n", fAngle, shhmmss_r(Ymds.isec, sTimeBuffer) );
      return;
    }

  printf( "================ Radar Data Header ================\n" );

  /* Separate time conversions, so there is no crosstalk/overflow */
  Ymds.isec = pDHeader_a->iDataTime/1000;
  Ymds.imills = MILLS_FROM_MS_FLAGS(pDHeader_a->iDataTime%1000,YMDS_FLG_UTC);
  printf( " HW15&16: %-12d (%s.%03d)\n", 
	  pDHeader_a->iDataTime, shhmmss_r(Ymds.isec, sTimeBuffer),
	  MS_FROM_MILLS(Ymds.imills) );
  YmdsFromNrTime( &Ymds, pDHeader_a->iDataDate, 0 );
  printf( " HW17&18: %-6d (%s)  %-6d (u range)\n", 
	  pDHeader_a->iDataDate, sddmonyyyy_r(Ymds.iyear,Ymds.imon, Ymds.iday,sTimeBuffer),
	  pDHeader_a->iUnambRange );
  fAngle = fPDegFromBin2(pDHeader_a->iAzimuth);
  printf( " HW19&20: %-6d (%6.2fAz)     %-6d (Ray number)\n", 
	  pDHeader_a->iAzimuth, fAngle, pDHeader_a->iRayNumber );
  fAngle = fElDegFromBin2(pDHeader_a->iElevation);
  switch( pDHeader_a->iRayStatus)
    {
    case NR_RAY_STATUS_START_SWEEP:
      strcpy( sText, "(SweepStart)" );
      break;
    case NR_RAY_STATUS_MIDDLE:
      strcpy( sText, "(Middle)" );
      break;
    case NR_RAY_STATUS_END_SWEEP:
      strcpy( sText, "(SweepEnd)" );
      break;
    case NR_RAY_STATUS_START_VOLUME:
      strcpy( sText, "(VolumeStart)" );
      break;
    case NR_RAY_STATUS_END_VOLUME:
      strcpy( sText, "(VolumeEnd)" );
      break;
    default: 
      strcpy( sText, "(UNKNOWN)" );
      break;
    }
  printf( " HW21&22: %-6d %-13s  %-6d (%5.2fEl)\n", 
	  pDHeader_a->iRayStatus, sText, pDHeader_a->iElevation, fAngle );
  printf( " HW23&24: %-6d (SweepNumber)  %-6d (Ref start)\n", 
	  pDHeader_a->iSweepNumber, pDHeader_a->iBinStartRef );
  printf( " HW25&26: %-6d (Dop start)    %-6d (Ref step)\n", 
	  pDHeader_a->iBinStartDop, pDHeader_a->iBinStepRef );
  printf( " HW27&28: %-6d (Dop step)     %-6d (Ref number)\n", 
	  pDHeader_a->iBinStepDop, pDHeader_a->iBinNumberRef );
  printf( " HW29&30: %-6d (Dop number)   %-6d (Sector number)\n", 
	  pDHeader_a->iBinNumberDop, pDHeader_a->iSectorNumber );
  printf( " HW31&32: %6.3f (Calibration constant)\n", 
	  pDHeader_a->fSystemCal );
  printf( " HW33&34: %-6d (Ref pointer)  %-6d (Vel pointer)\n", 
	  pDHeader_a->iRefPointer, pDHeader_a->iVelPointer );
  printf( " HW35&36: %-6d (Wid pointer)  %-6d (Vel res)\n", 
	  pDHeader_a->iWidPointer, pDHeader_a->iVelocityRes );
  printf( " HW37&38: %-6d (Vol pattern)  %-6d \n", 
	  pDHeader_a->iVolumePattern, pDHeader_a->unused1[0] );
  printf( " HW39&40: %-6d                %-6d \n", 
	  pDHeader_a->unused1[1], pDHeader_a->unused1[2] );
  printf( " HW41&42: %-6d                %-6d (Ref PB)\n", 
	  pDHeader_a->unused1[3], pDHeader_a->ref_ptrp );
  printf( " HW43&44: %-6d (Vel PB)       %-6d (Wid PB)\n", 
	  pDHeader_a->vel_ptrp, pDHeader_a->spc_ptrp );
  printf( " HW45&46: %-6d (Nyquist)      %-6d (Attenuation)\n", 
	  pDHeader_a->iNyquistVel, pDHeader_a->iAttenuation );
  printf( " HW47&48: %-6d (Min dif)      %-6d \n", 
	  pDHeader_a->min_dif, pDHeader_a->unused2[0] );
  printf( " HW49&50: %-6d                %-6d \n", 
	  pDHeader_a->unused2[1], pDHeader_a->unused2[2] );

  return;

}
/* =====================================================
 * Print the data
 */
static void PrintData
( const struct nr_packet *pRecord_a,  
  const struct view_options *pOptions_a )
{
  const struct nr_radar_data_header *pDHeader;
  if (!pOptions_a->lPrintData) return;

  pDHeader = &(pRecord_a->DataHeader);
  if (pDHeader->iRefPointer)
    {
      printf( "================ Reflectivity ================\n" );
      PrintOneData( &(pRecord_a->iData[pDHeader->iRefPointer-sizeof(struct nr_radar_data_header)]),
		    pDHeader->iBinNumberRef,
		    0.5, -33.0, pOptions_a->iLineLength );
    }

  if (pDHeader->iVelPointer)
    {
      FLT4 fGain, fOffset;
      printf( "================== Velocity ==================\n" );
      if (pDHeader->iVelocityRes==4)
	{
	  fGain = 1.0;
	  fOffset = -129.0;
	}
      else
	{
	  fGain = 0.5;
	  fOffset = -64.5;
	}
      PrintOneData( &(pRecord_a->iData[pDHeader->iVelPointer-sizeof(struct nr_radar_data_header)]),
		    pDHeader->iBinNumberDop, fGain, fOffset, pOptions_a->iLineLength );
    }

  if (pDHeader->iWidPointer)
    {
      printf( "=================== Width ===================\n" );
      PrintOneData( &(pRecord_a->iData[pDHeader->iWidPointer-sizeof(struct nr_radar_data_header)]),
		    pDHeader->iBinNumberDop, 0.5, -64.5, pOptions_a->iLineLength );
    }

  return;

}
/* =====================================================
 * Return a string having a guarenteed null termination after
 * the specified number of characters.
 */
static char *sTailString
(const char *sString_a,
 SINT4 iMaxSize_a )
{
  static char sString_c[80];
  SINT4 iSize;
  iSize = MIN( 79, iMaxSize_a );
  strncpy( sString_c, sString_a, iSize );
  sString_c[iSize] = 0;

  return sString_c;

}

/* ===========================================================
 * Print a line of ray data, using formats that vary with the type of the
 * parameter.
 */
static void PrintOneData
( const UINT1 iData_a[],
  SINT2 iBinCount_a,
  FLT4  fGain_a,
  FLT4  fOffset_a,
  SINT4 iLineLength_a )
{
  SINT4 iBin, iValue;
  SINT4 iTotalLength, iThisLength;
  char sThreshold[20];    /*String to display for threshold case */
  char sOverlaid[20];     /*String to display for overlaid case */
  char sFormat[20];       /*Format string for data printing */
  char sThisString[20];   /*String to print */
  

  strcpy(sThreshold, "  -.- " ) ;
  strcpy(sOverlaid, "  *.* " ) ;
  strcpy(sFormat, "%5.1f " ) ;

  printf( "%4d:", 0 ); 
  iTotalLength=5;
  for (iBin=0; iBin<iBinCount_a; iBin++)
    {
      iValue = iData_a[iBin];
      if( iValue == NR_NO_DATA )
	strcpy( sThisString, sThreshold ) ;
      else if( iValue == NR_OVERLAID )
	strcpy( sThisString, sOverlaid ) ;
      else
      	sprintf( sThisString, sFormat, (fGain_a*(FLT4)iValue)+fOffset_a ) ;
      iThisLength = strlen(sThisString);
      if( iTotalLength+iThisLength > iLineLength_a )
	{
	  printf( "\n%4d:", iBin );
	  iTotalLength=5;
	}

      printf( "%s", sThisString );
      iTotalLength += iThisLength;
    }
  
  printf( "\n" ) ;
  return;
}

/* =========================
 * Byte swapping functions.  Swap from/to the big endian format of the
 * concurent computers. 
 */
static void Pack2( UINT2 *pData2_a )
{
  UINT1 *pData1;
  UINT2 Data2;

  pData1    = (UINT1 *)pData2_a;
  Data2     = pData1[0];
  Data2     = Data2<<8;
  Data2    += pData1[1];
  *pData2_a = Data2;
}

static void Pack4( UINT4 *pData4_a )
{
  UINT1 *pData1;
  UINT4 Data4;

  pData1    = (UINT1 *)pData4_a;
  Data4     = pData1[0];
  Data4     = Data4<<8;
  Data4    += pData1[1];
  Data4     = Data4<<8;
  Data4    += pData1[2];
  Data4     = Data4<<8;
  Data4    += pData1[3];
  *pData4_a = Data4;
}

/* ==================================================================
 * Convert time formats.
 */
static void YmdsFromNrTime
( struct ymds_time *pYmds_a,
  UINT4 iDate_a,    /* Date in days */
  UINT4 iTime_a )   /* Time in ms */
{
  struct tm RayTm;
  time_t Time;
  
  Time = (iDate_a-1)*(24*60*60);
  Time += iTime_a / 1000;

  gmtime_r( &Time, &RayTm );

  memset( pYmds_a, 0, sizeof(*pYmds_a) );
  pYmds_a->iyear = RayTm.tm_year+1900;
  pYmds_a->imon = RayTm.tm_mon+1;
  pYmds_a->iday = RayTm.tm_mday;
  pYmds_a->isec = iPackTime( RayTm.tm_hour, RayTm.tm_min, RayTm.tm_sec );

  pYmds_a->imills = MILLS_FROM_MS_FLAGS(iTime_a % 1000, YMDS_FLG_UTC);
  return;
}
