/* ***************************** 
 * *                           * 
 * *      IRIS  SYSTEM         * 
 * * PRODUCT PRINTOUT UTILITY  * 
 * *                           * 
 * ***************************** 
 * File: iris/examiners/show_product.C
 * 
 *  COPYRIGHT (c) 1991, 1992, 1995, 1997, 1998, 1999, 2000, 2001
 *                 2002, 2003, 2004, 2005, 2006 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. 
 */ 
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

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

static const char* sOnOff(UINT4 iInput_a);

/* ================================================================
 * Print out information about a product file:
 * History:
 *     jmh   20 May 1992   Added file name and lat/long
 *     jmh   12 Dec 1990   Init
 * Entry parameters:
 *     phead_a - Product header
 *     isize_a - Size of the file
 *     sfile_a - File name
 * Exit parameters:
 *     lok_a   - OK flag
 */
void show_product
(const struct product_hdr *phead_a,
 const struct site_setup *pSite_a,   /* Can be NULL if not available */
 SINT4 isize_a,
 const char *sfile_a,
 UINT2 *lok_a )
{
  UINT4 iIndex;
  SINT4 iRadarHeight, iLowPRF;
  const struct product_configuration *pPcf;
  char sRecordTZName[TZNAME_SIZE], sLocalTZName[TZNAME_SIZE];
  char sTimeBuffer[21];
  *lok_a = TRUE;
  pPcf = &phead_a->pcf;

  printf("------------- Product Summary for %s -------------\n", sfile_a );

  if (isize_a<PRODUCT_HDR_SIZE)
    {
      printf("File size of %d is too small for header.\n", isize_a );
      *lok_a = FALSE;
      return;
    }

  /* Site names and locations
   */
  printf("Ingest site name    : '%s', Version: %s\n",
	 nullify( phead_a->end.sing_sitename, 16 ), phead_a->end.sing_version ); 

  {
    char sHardwareName[20];
    nullify_ncpy( sHardwareName, phead_a->end.sIngHardwareName, 16 );
    for (iIndex=0; iIndex<strlen(sHardwareName); iIndex++ )
      {
	if (!isprint(sHardwareName[iIndex])) sHardwareName[iIndex]='.';
      }
    printf( "Ingest hardware name: '%s'\n", sHardwareName );
  }
  printf("Product site name   : '%s', Version: %s\n",
	 nullify( phead_a->end.sprod_sitename, 16 ), phead_a->end.sprod_version ); 

  /* Print out some numbers based on the product header
   */
  printf("File size: %d bytes (Disk space: %d bytes)\n",
	 (int)phead_a->hdr.ibytes, isize_a );

  if( isize_a < phead_a->hdr.ibytes ) {
    printf(" File size is inconsistent.\n" ) ;
    *lok_a = FALSE ;
  }

  printf("Product type is: %s\n", sproduct_name16(pPcf->iptype) );

  printf("PCO name: %s,    ", nullify(pPcf->spname,12) );
  printf("TCO name: %s\n",    nullify(pPcf->stname,12) );

  /* PRF and Wavelength
   */
  iLowPRF = NINT( fPrfLowFromHighCase( phead_a->end.iprf, phead_a->end.itrig ));
  switch( phead_a->end.itrig )
    {
    default:
      printf( "PRF: %dHz", phead_a->end.iprf );
      break;
    case PRF_2_3:
    case PRF_3_4:
    case PRF_4_5:
      printf( "PRF: %d/%dHz", phead_a->end.iprf, iLowPRF );
      break;
    case PRF_SEQ0: printf( "PRF: Seq#0" ) ; break ;
    case PRF_SEQ1: printf( "PRF: Seq#1" ) ; break ;
    case PRF_SEQ2: printf( "PRF: Seq#2" ) ; break ;
    case PRF_SEQ3: printf( "PRF: Seq#3" ) ; break ;
    }
  printf( ", Wavelength: %4.2fcm, Nyquist: %4.2fm/s(V), %4.2fm/s(W)\n",
	  ((FLT4)phead_a->end.ilambda)/100.0,
	  fNyquistVelocity( phead_a->end.iprf, phead_a->end.itrig,
			    phead_a->end.ilambda, phead_a->end.ipolar ),
	  fNyquistWidth( phead_a->end.iprf, phead_a->end.ilambda,
			 phead_a->end.ipolar ) ) ;

  /* Polarization information */
  switch( phead_a->end.ipolar )
    {
    default:
      printf( "XMT Polarization: Horizontal" );
      break;
    case POL_VERT_FIX:
      printf( "XMT Polarization: Vertical" );
      break;
    case POL_ALTERNATING:
      printf( "XMT Polarization: Alternating" );
      break;
    case POL_SIMULTANEOUS:
      printf( "XMT Polarization: Simultaneous" );
      break;
    }
  printf(", Wind:");
  if ((phead_a->end.iWindSpeed==0)&&(phead_a->end.iWindDirection==0))
    {
      printf("???\n");
    }
  else
    {
      BIN2 iDirection;
      iDirection = ((UINT2)phead_a->end.iWindDirection)<<8;
      printf("%3.1fm/s @%ddeg.\n", ((FLT4)phead_a->end.iWindSpeed)/2.0, 
	     NINT(fPDegFromBin2(iDirection)) );
    }
  printf( "Constant:%.2f dB, I0:%.2f dBm, Cal Noise:%.2f dBm, Bandwidth:%d kHz.\n",
	  ((FLT4)phead_a->end.iRadarConstantHoriz)/100.0,
	  ((FLT4)phead_a->end.iI0Horiz)/100.0,
	  ((FLT4)phead_a->end.iCalNoiseHoriz)/100.0,
	  phead_a->end.iReceiverBandwidth );
  
  iRadarHeight = phead_a->end.ignd_hgt+phead_a->end.irad_hgt;
  printf( "Heights: Radar: %dm, Ground: %dm, Melting: ", 
	  iRadarHeight, phead_a->end.ignd_hgt );
  if (phead_a->end.iMeltingHeight==0)
    printf( "????m MSL\n" );
  else
    {
      SINT2 iMeltingHeight = (0x8000^phead_a->end.iMeltingHeight);
      printf( "%dm MSL\n", iMeltingHeight );
    }

  /* Information that is useful for most but not all products
   */
  if( pPcf->iptype != PROD_RAW )
    {
      FLT8 flat, flon;
      char sLatLon1[20], sLatLon2[20];
      
      printf("Size is: %dx%dx%d pixels\n", pPcf->ixsize, 
	     pPcf->iysize, pPcf->izsize );
      printf("Scale is: %5.2f x %5.2f x %5.2f m/pixel\n", 
	     ((double)pPcf->ixscale)/100.0,
	     ((double)pPcf->iyscale)/100.0,
	     ((double)pPcf->izscale)/100.0 );
      
      flat = fRadianFromBin4( phead_a->end.ilat_center);
      flon = fRadianFromBin4( phead_a->end.ilon_center);
      sLatFromRadian( flat, sLatLon1, 20 ) ;
      sLonFromRadian( flon, sLatLon2, 20 ) ;
      printf("Center Location: %s, %s, ref: %d meters\n", sLatLon1, sLatLon2,
	     iRadarHeight-phead_a->end.iReferenceOffset );
      printf( "Projection type is: %s\n", sProjectionName32(pPcf->iprojection_type) );
      flat = fRadianFromBin4( phead_a->end.ilat_ref);
      flon = fRadianFromBin4( phead_a->end.ilon_ref);
      sLatFromRadian( flat, sLatLon1, 20 ) ;
      sLonFromRadian( flon, sLatLon2, 20 ) ;
      printf("Projection Reference Point: %s, %s\n", sLatLon1, sLatLon2 );

      if (lProjectionHasStdPar(pPcf->iprojection_type) )
	{
	  flat = fRadianFromBin4( phead_a->end.iStdParallel1);
	  flon = fRadianFromBin4( phead_a->end.iStdParallel2);
	  sLatFromRadian( flat, sLatLon1, 20 ) ;
	  sLatFromRadian( flon, sLatLon2, 20 ) ;

	  printf("Projection Standard Parallels: %s, %s\n", sLatLon1, sLatLon2 );
	}
      printf("Equitorial Radius: %9.5f km, Flattening: 1/%6.5f\n", 
	     ((FLT8)phead_a->end.iEarthRadius)/100000.0,
	     ((FLT8)phead_a->end.iFlattening)/1000000.0 );

      printf("Radar position is: %3.1f, %3.1f pixels\n", 
	     ((double)pPcf->ixrad)/1000.0, ((double)pPcf->iyrad)/1000.0 );
      printf("Product data type is %s (%d)\n", sdata_name6(pPcf->idata_out),
	     (int)pPcf->idata_out );
      /* Display Z/R numbers only if it seems applicable based on the datatype */
      switch( pPcf->idata_out )
	{
	case DB_RAINRATE2:
	case DB_FLIQUID2:
	  if (pPcf->idata_in!=DB_KDP)
	    {
	      printf("Z/R:  Z = %5.1f * R ** %4.2f, Source: ", 0.001*(FLT4)pPcf->izr_const,
		     0.001*(FLT4)pPcf->izr_exp );
	      {
		UINT1 iActionFlag=0;
		if (pPcf->iflags & PF_ZR_SOURCE0) iActionFlag |= 1;
		if (pPcf->iflags & PF_ZR_SOURCE1) iActionFlag |= 2;
		
		switch( iActionFlag )
		  {
		  case 0:
		    printf("Type-In\n");
		    break;
		  case 1:
		    printf("Setup\n");
		    break;
		  case 2:
		    if (phead_a->end.iPEFlags & PEF_ZR_FALLBACK)
		      printf("Disdrometer (fallback to Setup)\n");
		    else
		      printf("Disdrometer\n");
		    break;
		  default:
		    printf("Unknown\n");
		    break;
		  }
	      }
	    }
	  else
	    {
	      printf("K/R:  K = %5.4f * R ** %4.2f, Source: ", 0.000001*(FLT4)pPcf->izr_const,
		     0.001*(FLT4)pPcf->izr_exp );
	    }
	  break;
	case DB_VIL2:
	  printf("Z/W:  Z = %5.1f * W ** %4.2f, Source: ", 0.001*(FLT4)pPcf->izr_const,
		 0.001*(FLT4)pPcf->izr_exp );
	  break;
	}
      printf("Color count:%d, Color set: %d", pPcf->colors.icolcnt,
	     0xff & pPcf->colors.iset_and_scale );
      if (pPcf->colors.iflags & COLOR_SCALE_VARIABLE)
	{
	  printf( ", variable\n" );
	  if (pPcf->colors.icolcnt)
	    {
	      struct data_convert Convert;
	      int iColor;
	      
	      PendFillDataConvert( &Convert, &phead_a->end, CONVERT_FLAGS_DEFAULT );
	      
	      printf("Seams:");
	      for (iColor=0; iColor<pPcf->colors.icolcnt; iColor++ )
		{
		  FLT4 fValue = fdata_to_user( pPcf->colors.ilevel_seams[iColor], 
					       pPcf->idata_out, &Convert );
		  printf( " %3.2f", fValue );
		}
	      printf("\n");
	    }
	}
      else
	{
	  printf( ", fixed, Start: %3.1f, Step: %3.1f\n",
		  ((FLT4)pPcf->colors.istart)/10.0, ((FLT4)pPcf->colors.istep)/10.0  );
	}
    }
  printf("Maximum range: %5.1f km\n", fMaxRangeFromPCF( pPcf ) );
  
  /* Print out product specific information.  This could be expanded a lot. */
  switch( pPcf->iptype )
    {
    case PROD_BEAM:
      printf( "BEAM limits: %3.1f to %3.1f Az, %3.1f to %3.1f El, %1.0f to %1.0f km\n",
	      fPDegFromBin4(pPcf->psi.beam.iaz_left), fPDegFromBin4(pPcf->psi.beam.iaz_right),
	      fElDegFromBin4(pPcf->psi.beam.iel_low), fElDegFromBin4(pPcf->psi.beam.iel_high),
	      ((FLT4)pPcf->psi.beam.irmin)/100000.0, ((FLT4)pPcf->psi.beam.irmax)/100000.0 );
      printf( "BEAM smoothing: %3.2f deg Az, %3.2f deg El\n",
	      fPDegFromBin4(pPcf->psi.beam.iaz_smooth),
	      fPDegFromBin4(pPcf->psi.beam.iel_smooth) );
      printf( "BEAM sun start pos: Az:%3.2f, El:%3.2f\n",
	      fPDegFromBin4(pPcf->psi.beam.iaz_sun_start),
	      fElDegFromBin4(pPcf->psi.beam.iel_sun_start) );
      break;
    case PROD_CAPPI:
      printf( "CAPPI bottom height: %d meters above reference\n",
	      (pPcf->psi.cappi.icappi_hgt+50)/100 );
      break;
    case PROD_CATCH:
      {
	const struct catch_psi_struct *pPsi=&pPcf->psi.Catch;
	UINT4 iSeconds;  char sBuf[12];
	UINT1 iRain1Flags;
	printf( "CATCH Flags: Warnings:%s\n",
		sOnOff(pPsi->iFlags&CATCH_FLG_WARNINGS_ON) );
	iRain1Flags = pPsi->iFlags>>16;
	printf( "RAIN1 Flags: CltMap:%s/%s Gage:%s/%s\n",
		sOnOff(iRain1Flags&RAIN_FLG_CLT_MAP),
		sOnOff(iRain1Flags&RAIN_FLG_CLT_MAP_OK),
		sOnOff(iRain1Flags&RAIN_FLG_GAG_COR),
		sOnOff(iRain1Flags&RAIN_FLG_GAG_COR_OK) );
	printf( "Using RAIN1 inputs called '%s'\n", nullify(pPsi->sProduct,12) );
	if (iRain1Flags&RAIN_FLG_GAG_COR_OK)
	  printf( "Average GAGE correction factor: %3.1f dB\n", 
		  fdata_to_user(0xffff&pPsi->iGageFactor, DB_DBZ2, NULL) );
	printf( "Subcatchment file: '%s'\n", pPsi->sSubCatchment );
	printf( "Threshold Offset %5.3f mm, Slope factor: %5.3f\n", 
		((FLT4)pPsi->iThresholdOffset)/1000.0, 
		((FLT4)pPsi->iThresholdFactor)/1000.0 );
	iSeconds  = 3600*pPsi->iHours;
	iSeconds += (0xffff & pPsi->iSpanSeconds);
	printf( "Time span: %s, Inputs: %d minutes\n", shhmm_r( iSeconds, sBuf ),
		((SINT4)pPsi->iRain1Span)/60);
      }
      break;
    case PROD_MAX:
      printf( "Bottom Height: %6.2f\n", 
	      ((double)(phead_a->pcf.psi.max.ibottom)) / 1E5 ) ;
      
      printf( "Top Height:    %6.2f\n", 
	      ((double)(phead_a->pcf.psi.max.itop)) / 1E5 ) ;
      
      printf( "Side Panel Size:  %d\n", phead_a->pcf.psi.max.iside_size ) ;
    case PROD_PPI:
      printf( "PPI elevation angle: %3.2f degrees\n",
	      fElDegFromBin2(pPcf->psi.ppi.ippi_el) );
      break;
    case PROD_RAIN1:
      {
	const struct rain_psi_struct *pPsi=&pPcf->psi.rain;
	UINT2 iInputPType;
	printf( "RAIN1 Flags: CltMap:%s/%s Gage:%s/%s Diagnostic:%s\n",
		sOnOff(pPsi->irain_flags&RAIN_FLG_CLT_MAP),
		sOnOff(pPsi->irain_flags&RAIN_FLG_CLT_MAP_OK),
		sOnOff(pPsi->irain_flags&RAIN_FLG_GAG_COR),
		sOnOff(pPsi->irain_flags&RAIN_FLG_GAG_COR_OK),
		sOnOff(pPsi->irain_flags&RAIN_FLG_DIAGNOSTIC) );
	if (pPsi->irain_flags&RAIN_FLG_SRI_INPUT) 
	  iInputPType = PROD_SRI;
	else
	  iInputPType = PROD_CAPPI;
	  
	printf( "Using %s inputs called '%s'\n", sproduct_name5(iInputPType),
		nullify(pPsi->sproduct,12) );
	if (pPsi->irain_flags&RAIN_FLG_GAG_COR_OK)
	  printf( "Average GAGE correction factor: %3.1f dB\n", 
		  fdata_to_user(pPsi->iAverageGageFactor, DB_DBZ2, NULL) );
	printf( "Time span: %d minutes\n", ((SINT4)(UINT4)pPsi->iSeconds)/60 );
      }
      break;
    case PROD_RAINN:
      {
	const struct rain_psi_struct *pPsi=&pPcf->psi.rain;
	UINT4 iSeconds;  char sBuf[12];
	printf( "RAIN1 Flags: CltMap:%s/%s Gage:%s/%s\n",
		sOnOff(pPsi->irain_flags&RAIN_FLG_CLT_MAP),
		sOnOff(pPsi->irain_flags&RAIN_FLG_CLT_MAP_OK),
		sOnOff(pPsi->irain_flags&RAIN_FLG_GAG_COR),
		sOnOff(pPsi->irain_flags&RAIN_FLG_GAG_COR_OK) );
	printf( "Using RAIN1 inputs called '%s'\n", nullify(pPsi->sproduct,12) );
	if (pPsi->irain_flags&RAIN_FLG_GAG_COR_OK)
	  printf( "Average GAGE correction factor: %3.1f dB\n", 
		  fdata_to_user(pPsi->iAverageGageFactor, DB_DBZ2, NULL) );
	iSeconds  = 3600*(UINT4)pPsi->irainn_hours;
	iSeconds +=      (UINT4)pPsi->iSeconds;
	printf( "Time span: %s, input: %d minutes\n", shhmm_r( iSeconds, sBuf ),
		((SINT4)pPsi->iRain1Span)/60 );
      }
      break;
    case PROD_RAW:
      if (pPcf->psi.raw.iflags&RAW_FLG_SWEEP)
	printf( "Single sweep #%d", pPcf->psi.raw.isweep );
      else
	printf( "Full volume scan" );
      switch(pPcf->psi.raw.iraw_convert)
	{
	case IRAW_CONVERT_PRESERVE:
	  printf( ", Preserve resolution" );
	  break;
	case IRAW_CONVERT_8_TO_16:
	  printf( ", Force 16-bit" );
	  break;
	case IRAW_CONVERT_16_TO_8:
	  printf( ", Force 8-bit" );
	  break;
	default:
	  printf( ", Unknown resolution" );
	  break;
	}
      if (pPcf->psi.raw.iflags&RAW_FLG_DATAMASK)
	printf( ", Selected data recorded\n" );
      else
	printf( ", All data recorded\n" );
      break;
    case PROD_RTI:
      printf( "Data time span: %3.1f-%3.1f seconds\n",
	      ((FLT4)pPcf->psi.rti.iStartOffset)/1000.0,
	      ((FLT4)pPcf->psi.rti.iEndOffset)/1000.0 );
      if ((((BIN4)IMP_WILD_ANGLE2)<<16) == pPcf->psi.rti.iSweepAngle )
	printf( "Sweep angle: *\n" );
      else
	printf( "Sweep angle: %3.2f degrees\n",
		fPDegFromBin4(pPcf->psi.rti.iSweepAngle) );
      break;
    case PROD_SRI:
      {
	const struct sri_psi_struct *pPsi=&pPcf->psi.Sri;
	printf( "SRI Flags: Profile:%s  Convection:%s Clutter:%s Melt src: ",
		sOnOff(pPsi->iPsiFlags&SRI_FLG_PROFILE_CORRECTION),
		sOnOff(pPsi->iPsiFlags&SRI_FLG_CHECK_CONVECTION),
		sOnOff(!(pPsi->iPsiFlags&SRI_FLG_NO_CLUTTER)) );
	if (!(pPsi->iPsiFlags&SRI_FLG_MELT_SOURCE1))
	  {
	    if (!(pPsi->iPsiFlags&SRI_FLG_MELT_SOURCE0))
	      /* Case 0: */
	      printf( "Ingest\n" );
	    else
	      /* Case 1: */
	      printf( "Setup\n" );
	  }
	else
	  {
	    if (!(pPsi->iPsiFlags&SRI_FLG_MELT_SOURCE0))
	      /* Case 2: */
	      printf( "Type-In\n" );
	    else
	      /* Case 3: */
	      printf( "Unknown\n" );
	  }
	
	printf( "Surface height: %3.1f km, max height: %3.1f km\n",
		((FLT4)pPsi->iSurfaceHeight)/1000.0, ((FLT4)pPsi->iMaxHeight)/1000.0 );
	
	printf( "Profile: gradients: %3.1f(above) %3.1f(below) dB/km, \n",
		((FLT4)pPsi->iGradientAbove)/100.0, ((FLT4)pPsi->iGradientBelow)/100.0 );
	printf( "         Melting: " );
	if (0 == pPsi->iMeltHeight)
	  printf( "Unknown" );
	else
	  {
	    SINT2 iMeltHeight=(0x8000^pPsi->iMeltHeight);
	    printf( "%d meters", iMeltHeight );
	  }
	printf( ", Intensity: %3.1fdB, Thickness: %d meters\n",
		((FLT4)pPsi->iMeltIntensity)/100.0, pPsi->iMeltThickness );
	printf( "Convective threshold: %3.1f dBZ, height: %3.1f km above melting\n",
		fdata_to_user(pPsi->iConvectiveZ, DB_DBZ2, NULL),
		((FLT4)pPsi->iConvectiveHeight)/1000.0 );
	printf( "Bins: Total: %d, Data: %d, Corrected: %d\n",
		pPsi->iBinCount, pPsi->iDataCount, pPsi->iCorrCount );
      }
      break;

    case PROD_TDWR:
      printf( "Source ID:'%4.4s', Data Sources:", pPcf->psi.tdwr.sSourceID );
      if ( pPcf->psi.tdwr.iFlags & TDWR_FLG_LLWAS) printf( " LLWAS" );
      if ( pPcf->psi.tdwr.iFlags & TDWR_FLG_WARN ) printf( " WARN" );
      if ( pPcf->psi.tdwr.iFlags & TDWR_FLG_SLINE) printf( " SLINE" );
      printf( "\n" );
      printf( "Center field wind:%3.3sdegrees %2.2s Knots Gusting to %2.2s\n",
	      pPcf->psi.tdwr.sCFDirection, 
	      pPcf->psi.tdwr.sCFSpeed, pPcf->psi.tdwr.sCFGustSpeed );
      printf( "Forecast Time: %s\n", shhmmss_r(pPcf->psi.tdwr.iForecastTime, sTimeBuffer) );
      break;

    case PROD_VVP:
      printf( "VVP Range Interval: %4.1f to %4.1f km, Bin quota: %d\n", 
	      ((FLT4)pPcf->psi.vvp.ivvp_rmin)/100000.0,
	      ((FLT4)pPcf->psi.vvp.ivvp_rmax)/100000.0,
	      pPcf->psi.vvp.ivvp_quota );
      printf( "%5d Heights from: %4.1f to %4.1f km, Unfolding:", 
	      pPcf->psi.vvp.ivvp_intervals,
	      ((FLT4)pPcf->psi.vvp.ivvp_hmin)/100000.0,
	      ((FLT4)pPcf->psi.vvp.ivvp_hmax)/100000.0 );
      if (pPcf->psi.vvp.ivvp_switch&VVP_UNFOLD)
	printf( " On\n" );
      else
	printf( " Off\n" );
      break;

    case PROD_WARN:
      printf( "Warning input area: %5.2f square km, symbol:%s\n",
	      ((FLT4)pPcf->psi.warn.iwarn_area)/1000000.0, 
	      nullify(pPcf->psi.warn.swarn_symbol, 12) );
      printf( "    PType: %12s", sproduct_name5(pPcf->psi.warn.iwarn_prods[0]) );
      printf( " %12s",       sproduct_name5(pPcf->psi.warn.iwarn_prods[1]) );
      printf( " %12s\n",     sproduct_name5(pPcf->psi.warn.iwarn_prods[2]) );
      printf( "    pName: %12s", nullify(pPcf->psi.warn.swarn_names[0],12) );
      printf( " %12s",       nullify(pPcf->psi.warn.swarn_names[1],12) );
      printf( " %12s\n",     nullify(pPcf->psi.warn.swarn_names[2],12) );
      printf( "Threshold: %12.2f %12.2f %12.2f\n", 
	      ((FLT4)pPcf->psi.warn.iwarn_threshs[0])/100.0,
	      ((FLT4)pPcf->psi.warn.iwarn_threshs[1])/100.0,
	      ((FLT4)pPcf->psi.warn.iwarn_threshs[2])/100.0 );
      printf( "    Times: %12s", shhmmss_r(pPcf->psi.warn.iwarn_seconds[0],sTimeBuffer) );
      printf( " %12s",           shhmmss_r(pPcf->psi.warn.iwarn_seconds[1],sTimeBuffer) );
      printf( " %12s\n",         shhmmss_r(pPcf->psi.warn.iwarn_seconds[2],sTimeBuffer) );
      break;

    }

  TZNameBuild( sRecordTZName, phead_a->end.sLocalTZName, phead_a->end.iMinutesWest,
	       pPcf->ing_time.imills );
  TZNameBuild( sLocalTZName, phead_a->end.sLocalTZName, phead_a->end.iLocalWest, 0 );
  printf("\n     Ingest time: %s %s (%d minutes west)\n", 
	 shhmmssddmonyyyy_r( &pPcf->ing_time, sTimeBuffer ), sRecordTZName, 
	 phead_a->end.iMinutesWest );
  printf("Volume scan time: %s %s (LT: %s %d minutes)\n",
	 shhmmssddmonyyyy_r( &pPcf->epoch, sTimeBuffer ), sRecordTZName, sLocalTZName, 
	 phead_a->end.iLocalWest );
  {
    char sTZ[8];
    if (phead_a->end.old_ing.imills & YMDS_FLG_UTC)
      strcpy( sTZ, "UTC" );
    else
      strcpy( sTZ, "LT" );
    printf(" Oldest Ing time: %s %s\n", 
	   shhmmssddmonyyyy_r( &phead_a->end.old_ing, sTimeBuffer ), sTZ );
    if (pPcf->prod_time.imills & YMDS_FLG_UTC)
      strcpy( sTZ, "UTC" );
    else
      strcpy( sTZ, "LT" );
    printf("Product Gen time: %s %s \n", 
	   shhmmssddmonyyyy_r( &pPcf->prod_time, sTimeBuffer ), sTZ );
  }
  printf( "Input count: %d\n", phead_a->end.input_cnt );
  if (pPcf->iflags&PF_COMPOSITED)
    {
      printf( "Product is composited, Site mask: 0x%08x", phead_a->end.iSiteMask );
      if (pSite_a)
	{
	  UINT4 iSiteMask, iSiteNumber;
	  iSiteMask = phead_a->end.iSiteMask;
	  iSiteNumber = 0;
	  while(iSiteMask && (iSiteNumber<MAX_SITES))
	    {
	      if (1&iSiteMask)
		{
		  if (pSite_a->sites[iSiteNumber].site_name16[0])
		    printf(" %3.3s", pSite_a->sites[iSiteNumber].site_name3 );
		  else
		    printf(" ???" );
		}
	      iSiteMask = iSiteMask>>1;
	      iSiteNumber++;
	    }
	}
      printf( "\n" );
    }
  else
    {
      printf( "Product is not composited.\n" );
    }

  printf("\n");
}
/* ================================
 * Function to return "On" or "Off".
 */
static const char* sOnOff(UINT4 iInput_a)
{
  static const char *sOn ="On ";
  static const char *sOff="Off";
  if (iInput_a)
    return sOn;
  else
    return sOff;
}
