/* ************************************************************************
 * *                                                                      *
 * *  Function to convert an IRIS product to a 4-bits/pixel IRIS picture  *
 * *              product in the circa V7.00 format.                      *
 * *                                                                      *
 * ************************************************************************
 * File: iris/output/Picture700.C
 *
 *                  COPYRIGHT (c) 1999, 2000, 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.
 */

/* This program is a pipe which takes an IRIS product in the standard input
 * and writes a modified product out the standard output.  It takes command
 * line control inputs consisting of color level control.  The output has
 * the inputs product header (with some possible level control changes),
 * followed by a data array packed in 4 bits-per-pixel.  These four bits
 * have the following interpretation:
 * 0 - Area-not-scanned, for example the corners of a PPI display.
 * 1 - Data-below-threshold, for example clear weather
 * 2-15 - Up to 14 data levels, in order of intensity, with 2 the weakest
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#define PF_PICTURE_OLD  (0x0001)  /* Old bit used to flag picture products */
struct private_scale_def
{
  UINT4 iflags;
  SINT2 icolcnt;
  UINT2 ilevel_seams[16];
};

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

static void Usage( void );

static MESSAGE ProcessProductHeader
(struct product_hdr *pheader_a,
 union level_control *plevel_a,  /* Returns level control structure */
 const struct private_scale_def *pScaleDef_a
 );

static void ComputeColormap
(UINT2 idtype_a,                 /* Data type */
 struct color_scale_def *colors_a,   /* Always modified */
 struct product_end *pend_a,
 union level_control *plevel_a,
 struct scale_text newscale_a[16],
 const struct private_scale_def *pPrivateScale_a );

static void local_colormap
(UINT2 iparam_a,
 struct color_scale_def *scaledef_a,
 struct scale_text newscale_a[16],
 const struct private_scale_def *pPrivateScale_a,
 union level_control *level_a,
 const struct data_convert *pconvert_a );

static void patchup_color_scale
(struct color_scale_def *scaledef_a,
 UINT2 icolscale_a[16],
 const struct private_scale_def *pPrivateScale_a );

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  struct product_hdr ProdHeader;
  union level_control Level;
  SINT4 iRes, iLine;
  UINT1 *pInBuffer, *pOutBuffer;
  MESSAGE istatus;
  struct private_scale_def ScaleDefinition;
  SINT4 iCount;

  setlocale( LC_ALL, "" ) ;

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

  /* Read in the header */
  iCount =  fread( &ProdHeader, sizeof(ProdHeader), 1, stdin );
  if (iCount != 1) fprintf( stderr, "Bad read, got%d, wanted%d\n",
					     iCount, sizeof(ProdHeader));

  /* Fill in the saturation bits from the product header.  This should
   * be added to the command line eventually.
   */
  if (ProdHeader.pcf.colors.iflags&COLOR_SCALE_TOP_SAT)
    ScaleDefinition.iflags |= COLOR_SCALE_TOP_SAT;
  if (ProdHeader.pcf.colors.iflags&COLOR_SCALE_BOT_SAT)
    ScaleDefinition.iflags |= COLOR_SCALE_BOT_SAT;

  istatus = ProcessProductHeader( &ProdHeader, &Level, &ScaleDefinition );
  if (istatus != SS_NORMAL) exit(EXIT_FAILURE);

  /* Write out the header, scale information may be changed. */
  iCount = fwrite( &ProdHeader, sizeof(ProdHeader), 1, stdout );
  if (iCount != 1) fprintf( stderr, "Bad write\n" );

  /* Malloc input and output buffers */
  iRes = idata_resolution( ProdHeader.pcf.idata_out );
  pInBuffer  = (UINT1*)malloc( ProdHeader.pcf.ixsize*iRes/8 );
  pOutBuffer = (UINT1*)malloc( (ProdHeader.pcf.ixsize+1)/2 );

  /* Now copy the data, converting to 4-bits/pixel */
  for (iLine=0; iLine<ProdHeader.pcf.iysize; iLine++)
    {
      iCount = fread( pInBuffer, iRes/8, ProdHeader.pcf.ixsize, stdin );
      if (iCount != ProdHeader.pcf.ixsize) fprintf( stderr, "Bad read\n" );
      levelize_4bit( pInBuffer, ProdHeader.pcf.ixsize, &Level, iRes, pOutBuffer );
      iCount = fwrite( pOutBuffer, (ProdHeader.pcf.ixsize+1)/2, 1, stdout );
      if (iCount != 1) fprintf( stderr, "Bad write\n" );
    }
  exit( EXIT_SUCCESS );
}
/* ======================================================================
 * Function to process the command line arguments.
 */
static void ProcessCommandLine
(int iArgc_a, 
 char *sArgv_a[],
 struct private_scale_def *pScaleDef_a )
{

  int iArg, iIndex;

  /* Zero the scale, and fill in some default numbers */
  memset( pScaleDef_a, 0, sizeof(pScaleDef_a) );
  pScaleDef_a->icolcnt = 14;
  /* These number assume we have a 16-bit data type available */
  for (iIndex=0; iIndex<16; iIndex++)
    {
      pScaleDef_a->ilevel_seams[iIndex] = 1+ (16*256*iIndex);
    }
  pScaleDef_a->iflags = COLOR_SCALE_VARIABLE;
  /* Note that the top and bottom saturation flags are picked up from the file
   * pScaleDef_a->iflags = COLOR_SCALE_VARIABLE|COLOR_SCALE_TOP_SAT|COLOR_SCALE_BOT_SAT;
   */

  /* 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 (0 == strncmp( sArg, "-seams", 6 ) )
	{
	  char sString[110], *sSubArg;
	  int iValue;

	  strncpy( sString, &sArg[6], 109); sString[109]=0;

	  for (iIndex=0; iIndex<16; iIndex++)
	    {
	      if (iIndex==0)
		sSubArg = strtok( sString, ":" );
	      else
		sSubArg = strtok( NULL, ":" );
	      if (sSubArg==NULL) Usage();
	      if( sscanf( sSubArg, "%d", &iValue ) != 1 )
		{
		  fprintf( stderr, "Could not parse '%s' after '%s'\n", sSubArg, sArg ) ;
		  Usage() ;
		}
	      pScaleDef_a->ilevel_seams[iIndex] = iValue;	      
	    }
	}

      else if (0 == strncmp( sArg, "-colors", 7) )
	{
	  char sString[10], *sSubArg;
	  int iValue;
	  strncpy( sString, &sArg[7], 9); sString[9]=0;
	  sSubArg = strtok( sString, ":" );
	  if (sSubArg==NULL) Usage();
	  if( sscanf( sSubArg, "%d", &iValue ) != 1 )
	    {
	      fprintf( stderr, "Could not parse '%s' after '%s'\n", sSubArg, sArg ) ;
	      Usage() ;
	    }
	  pScaleDef_a->icolcnt = MAX( 2, MIN( 14, iValue));	      
	}

      else if (0 == strncmp( sArg, "-h", 2))
	{
	  Usage();
	}
      
    }
  return;
}
/* ==================================================
 */
static void Usage( void )
{
  fprintf( stderr, "Options: -levels:N1:N2:N3:...:N16\n" );
  fprintf( stderr, "         -colors:N\n" );
  fprintf( stderr, "         -help\n" );
  fprintf( stderr, "         (all other options ignored)\n" );
  exit(EXIT_FAILURE) ;
}
/* =======================================================================
 * Check for some consistency.  The Product type must be valid, and the file
 * size must be large enough to hold the data expected.  Also generate the
 * colormap, and latch some output options.  All errors are signalled internally,
 * this allows for warnings in which we still output the file.
 */
static MESSAGE ProcessProductHeader
(struct product_hdr *pheader_a,
 union level_control *plevel_a,  /* Returns level control structure */
 const struct private_scale_def *pScaleDef_a
 )
{
  MESSAGE istatus;
  SINT4 isrc_size[3];
  SINT4 iCartesianSize;        /* Size calculated based on header */
  struct scale_text scale[16];

  memset( &scale, 0, sizeof(scale) );
  switch (pheader_a->pcf.iptype)
    {
    default:
      fprintf( stderr, "Product type not supported\n" );
      istatus = USER_ERROR_SIG;
      break;

    case PROD_BEAM:
    case PROD_CAPPI:
    case PROD_CROSS:
    case PROD_MAX:
    case PROD_PPI:
    case PROD_RAIN1:
    case PROD_RAINN:
    case PROD_RHI:
    case PROD_RTI:
    case PROD_SHEAR:
    case PROD_TOPS:
    case PROD_USER:
    case PROD_USERV:
    case PROD_VIL:
      isrc_size[0] = pheader_a->pcf.ixsize;
      isrc_size[1] = pheader_a->pcf.iysize;
      isrc_size[2] = pheader_a->pcf.izsize;
      if( ( isrc_size[0] < MIN_PRODUCT_SIZE ) ||
	  ( isrc_size[0] > MAX_PRODUCT_SIZE ) ||
	  ( isrc_size[1] < MIN_PRODUCT_SIZE ) ||
	  ( isrc_size[1] > MAX_PRODUCT_SIZE ) )
	{
	  sig_signal_va( IRIS_PROD_XYSIZE, 2, isrc_size[0], isrc_size[1] ) ;
	  return( USER_ERROR_SIG ) ;
	}
      if (isrc_size[2]==0) isrc_size[2]=1;
      if( ( isrc_size[2] < 0 ) ||
	  ( isrc_size[2] > MAX_PRODUCT_SURFACES ) )
	{
	  sig_signal_va( IRIS_PROD_ZSIZE, 1, isrc_size[2] ) ;
	  return( USER_ERROR_SIG ) ;
	}
      
      {
	SINT4 ires;
	ires = idata_resolution( pheader_a->pcf.idata_out );
	iCartesianSize = isrc_size[0]*isrc_size[1]*(ires/8)*isrc_size[2];
	if ( (iCartesianSize+PRODUCT_HDR_SIZE) > pheader_a->hdr.ibytes )
	  {
	    fprintf( stderr, "File size %d bytes is too small to contain expected data %d bytes\n",
		     pheader_a->hdr.ibytes, iCartesianSize );
	    return USER_ERROR_SIG;
	  }
	
	/* Note that this computes the color mapping table and modifies the header
	 * to use the current color map.
	 */
	ComputeColormap( pheader_a->pcf.idata_out, &pheader_a->pcf.colors, 
			 &pheader_a->end, plevel_a, scale, pScaleDef_a );
      }
      istatus = SS_NORMAL;
      break;
    }
  /* Set old picture bit */
  pheader_a->pcf.iflags |= PF_PICTURE_OLD;
  /* Set new file size */
  pheader_a->hdr.ibytes = 
    PRODUCT_HDR_SIZE + ((isrc_size[0]+1)/2)*isrc_size[1]*isrc_size[2];

  return istatus;
}

/* ============================================================================
 * This subroutine computes the color map and the color parameter scale
 * text from the product header.
 */
static void ComputeColormap
(UINT2 idtype_a,                    /* Data type */
 struct color_scale_def *colors_a,  /* Fill in color seam information */
 struct product_end *pend_a,
 union level_control *plevel_a,     /* Filled in so we can levelize */
 struct scale_text newscale_a[16],  /* Label text is here */
 const struct private_scale_def *pPrivateScale_a )
{
  struct data_convert convert;
  int iIndex;

  /* ----------------------------------------------------------------------- */
  /* Skip for invalid data types
   */
  if (idtype_a == 0) return;

  FillDataConvert( &convert, pend_a->iprf, pend_a->itrig, pend_a->ilambda, pend_a->ipolar,
		   CONVERT_FLAGS_DEFAULT ) ;

  local_colormap(idtype_a, colors_a, newscale_a, pPrivateScale_a, plevel_a, &convert );
  for (iIndex=0; iIndex<14; iIndex++)
    pend_a->istep_colors[iIndex] = iIndex+2;
  pend_a->istep_colors[14] = 15;
  pend_a->istep_colors[15] = 15;
}

/* ============================================================================
 * This subroutine returns information about a parameter that will help display
 * it.  
 */
static void local_colormap
(UINT2 iparam_a,                        /* Data type */
 struct color_scale_def *scaledef_a,    /* Returns color seam info for header */
 struct scale_text newscale_a[16],      /* Returns Array of text for possible legend use */
 const struct private_scale_def *pPrivateScale_a,
 union level_control *level_a,          /* Returns level control inforamation */
 const struct data_convert *pconvert_a ) /* Information to help convert data */
{   
  UINT4 idtype, index;
  UINT2 iColorScale[COLOR_MAP_ZOOM][16];
  UINT1 iColorNoData, iColorThresh;
  UINT2 iOldColorScale[16];
  /* ----------------------------------------------------------------------- */

  idtype = iparam_a;

  patchup_color_scale( scaledef_a, iOldColorScale, pPrivateScale_a);

  /* Copy color scale to a zoomed version, replicate for picture products */
  for (index=0; index<16; index++)
    {
      iColorScale[0][index] = iOldColorScale[index];
      iColorScale[1][index] = iOldColorScale[index];      
    }
  iColorNoData = 0;
  iColorThresh = 1;
  generic_map( idtype, scaledef_a, iColorScale, iColorThresh, iColorNoData, 
	       level_a, pconvert_a, newscale_a, NULL );

}

/* ======================================================
 * Fill in color array, and optionally override the whole thing
 */
static void patchup_color_scale
(struct color_scale_def *scaledef_a,
 UINT2 icolscale_a[16],
 const struct private_scale_def *pPrivateScale_a )

{
  SINT4 icolor;
  /* ----------------------------------------------------------------------- */

  scaledef_a->iflags  = COLOR_SCALE_VARIABLE | pPrivateScale_a->iflags;
  scaledef_a->istart  = 0;
  scaledef_a->istep   = 0;
  scaledef_a->icolcnt = pPrivateScale_a->icolcnt;
  /* Leave set_and_scale unchanged */
  for (icolor=0; icolor<16; icolor++)
    scaledef_a->ilevel_seams[icolor] =  pPrivateScale_a->ilevel_seams[icolor];

  /* Fill in color permutation table when making picture products
   */
  for (icolor=0; icolor<16; icolor++)
    icolscale_a[icolor] = MIN( icolor+2, 15 );
}


