/* ***********************************************************
 * *                                                         *
 * *  Function to convert an IRIS product to ASTERIX format  *
 * *                                                         *
 * ***********************************************************
 * File: utils/asterix/IrisToAsterix.C
 *
 *                   COPYRIGHT (c) 2003, 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 is a pipe which takes an IRIS 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 <errno.h>
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "sigtypes.h"
#include "projection.h"
#include "setup.h"
#include "headers.h"
//#include "ingest.h"
#include "product.h"
#include "user_lib.h"
#include "dsp_lib.h"
#include "misc_lib.h"

#include "SigAsterix.h"

struct cartesian_vectors
{
  UINT1 iFieldSpec;
  UINT1 iSAC;
  UINT1 iSIC;
  UINT1 iMessageType;
  UINT1 iVectorQualifier;
  UINT1 iRepeats;
  struct ast_single_vector Vector[255];
};

struct convert_state
{
  struct ast_data_block *pBlock;
  SINT4 iBlockSize;    /* Number of bytes used in the block so far */
  SINT4 iVectorCount;  /* Total number of vectors written to the file */
};

/* Information from the setup file useful in the conversion */
struct convert_config CConfig_c;

/* Information from the product useful in the conversion */
struct convert_info
{
  SINT4 iScalingFactor;
  SINT4 iCenterX;
  SINT4 iCenterY;
  struct ymds_time IngTimeUtc;
  union level_control Level;
  const struct convert_site_table *pSite;
};

/* Information from the command line */
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 CheckProductHeader
(const struct product_hdr *pheader_a,
 struct convert_info *pConvert_a );

static const struct convert_site_table * FindSiteByName
( const char *sSitename_a,             /* IRIS sitename, NULL terminated */
  const struct convert_config *pCConfig_a ); /* Configuration */

static void ConvertToAsterix
(const struct cartesian_product *pProd_a,
 struct convert_info *pConvert_a );

static void ConvertProductData
( const struct cartesian_product *pProd_a,
  const struct convert_info *pConvert_a,
struct convert_state *pState_a );

static void VectorizeLine
( SINT4 iYPos_a,           /* Signed Y-component position */
  const UINT1 *pBuffer_a,  /* Buffer of data */  
  SINT4 iXSize_a,          /* Number of pixels */
  struct cartesian_vectors Vectors_a[8],  /* Buffers holding vector info */
  const struct convert_info *pConvert_a,
  struct convert_state *pState_a );

static void AddVector
( SINT4 iXComp_a,
  SINT4 iYComp_a,
  SINT4 iRunLength_a,
  struct cartesian_vectors *pVectors_a );

static void ComputeColormap
(UINT2 idtype_a,                    /* Data type */
 const 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 );

static void WriteVectors
( cartesian_vectors *pVectors_a, 
  struct convert_state *pState_a );

static void WriteSOP
( const struct convert_info *pConvert_a,
  struct convert_state *pState_a );

static void WriteEOP
( const struct convert_info *pConvert_a,
  struct convert_state *pState_a );

static void WriteOneRecord
( const char *sBuffer_a,
  SINT4 iSize_a,
  struct convert_state *pState_a );

static void FlushDataBlock
( struct convert_state *pState_a );

static MESSAGE SignalHandler
(MESSAGE iStatus_a,
 UINT4 iCount_a,
 const UINT4 iSigArgs_a[] );

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  struct product_hdr ProdHeader;
  struct scale_text Scale[17];  /* Label text is here (unused) */
  struct private_scale_def ScaleDef;
  struct cartesian_product *pInProd;
  MESSAGE istatus;
  SINT4 iCount, iWantCount ;
  char sLog[200], sProdTime[TIMENAME_SIZE];
  struct convert_info Convert;

  setlocale( LC_ALL, "" ) ;

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

  /* Process the setup file, if any */
  LoadSetup( &CConfig_c, "IrisToAsterix.conf", 0 );

  /* 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, "IrisToAsterix.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 */
  sig_establish( SignalHandler, "IrisToAsterix" );
    
  /* Check for no sites in the config file */
  if (CConfig_c.iSiteCount<1)
    {
      Lprintf( &CConfig_c, "ERROR: No SITE line in the IrisToAsterix.conf file\n" );
      exit(EXIT_FAILURE);
    }

  /* Read in the header */
  iCount =  fread( &ProdHeader, 1, sizeof(ProdHeader), stdin );
  if (iCount != sizeof(ProdHeader)) 
    {
      sprintf( sLog, "ERROR: Bad read, got%d, wanted%d\n", iCount, sizeof(ProdHeader));
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }

  istatus = CheckProductHeader( &ProdHeader, &Convert );
  if (istatus != SS_NORMAL) exit(EXIT_FAILURE);

  /* Malloc input buffer */
  pInProd  = (struct cartesian_product*)malloc( ProdHeader.hdr.ibytes );
  if (!pInProd)
    {
      sig_signal(errno_to_message(errno));
      exit(EXIT_FAILURE);
    }
  pInProd->Header = ProdHeader;

  iWantCount = ProdHeader.hdr.ibytes-sizeof(ProdHeader);
  iCount =  fread( (&pInProd->Header)+1, 1, iWantCount, stdin );
  if (iCount != iWantCount)
    {
      sprintf( sLog, "ERROR: Bad read2, got%d, wanted%d\n", iCount, iWantCount);
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }
  
  /* Force saturation at the top, threshold if below the bottom.
   */
  ScaleDef.iflags |= COLOR_SCALE_TOP_SAT;

  /* Compute color mapping tables */
  ComputeColormap( pInProd->Header.pcf.idata_out, &pInProd->Header.end, 
		   &(Convert.Level), Scale, &ScaleDef );

  ConvertToAsterix( pInProd, &Convert );

  shhmmssddmonyyyy_r( &ProdHeader.pcf.ing_time, sProdTime );
  sprintf( sLog, "Converted %s product: %s, Date: %s\n", 
	   sproduct_name5(ProdHeader.pcf.iptype), 
	   nullify(ProdHeader.pcf.spname,12), sProdTime );
  Lprintf( &CConfig_c, sLog );
  
  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;
  /* Default scale is a reasonable scale for dBZ */
  for (iIndex=0; iIndex<16; iIndex++)
    {
      pScaleDef_a->ilevel_seams[iIndex] = 32767 + (500*iIndex) - 1000 ;
    }
  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. 
 */
static MESSAGE CheckProductHeader
(const struct product_hdr *pheader_a,
 struct convert_info *pConvert_a )
{
  char sLog[200];
  char sSitename20[20];

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

  /* Make sure that this file really looks like a product.  Check
   * the ID in the product.
   */
  if (pheader_a->hdr.id != ST_PRODUCT_HDR)
    {
      Lprintf( &CConfig_c, "ERROR: File header contains invalid ID.\n" ) ;
      return USER_ERROR_SIG;
    }

  /* Check that the data type is valid.  We only support data types
   * which can map to an intensity scale. */
  switch( pheader_a->pcf.idata_out)
    {
    case DB_DBT:
    case DB_DBT2:
    case DB_DBZ:
    case DB_DBZ2:
    case DB_DBZC:
    case DB_DBZC2:
    case DB_RAINRATE2:
      break;

    default:
      sprintf( sLog, "ERROR: Data type %s is not supported.\n", 
	       sdata_name6(pheader_a->pcf.idata_out) );
      Lprintf( &CConfig_c, sLog );
      return USER_ERROR_SIG;
    }

  /* Check that the product type is valid */
  switch( pheader_a->pcf.iptype)
    {
    case PROD_CAPPI:
      break;
    case PROD_PPI:
      break;
    case PROD_SRI:
      break;
    default:
      sprintf( sLog, "ERROR: Product type %s (%d) not supported.\n", 
	       sproduct_name5(pheader_a->pcf.iptype), pheader_a->pcf.iptype );
      Lprintf( &CConfig_c, sLog );
      return USER_ERROR_SIG;
    }

  /* Check that the projection type is valid */
  switch( pheader_a->pcf.iprojection_type)
    {
    case MAPROJ_AZIMUTHAL:
      break;
    default:
      sprintf( sLog, "ERROR: Projection type %s not supported.\n", 
	       sProjectionName32(pheader_a->pcf.iprojection_type) );
      Lprintf( &CConfig_c, sLog );
      return USER_ERROR_SIG;
    }

  /* Since all coordinate locations must be specified using a signed 
   * 8-bit number offset from the radar, check that all pixels are 
   * within such limits. */
  pConvert_a->iCenterX = (500+pheader_a->pcf.ixrad)/1000;
  pConvert_a->iCenterY = (500+pheader_a->pcf.iyrad)/1000;
  {
    SINT4 iMinX, iMaxX, iMinY, iMaxY;
    iMinX = 0-pConvert_a->iCenterX;
    iMinY = 0-pConvert_a->iCenterY;
    iMaxX = pheader_a->pcf.ixsize-pConvert_a->iCenterX;
    iMaxY = pheader_a->pcf.iysize-pConvert_a->iCenterY;
    if ((iMinX<-128)||(iMinX>127)||(iMinY<-128)||(iMinY>127)||
	(iMaxX<-128)||(iMaxX>127)||(iMaxY<-128)||(iMaxY>127))
      {
	sprintf( sLog, "ERROR: Product span of (%d,%d) to (%d,%d) exceeds max of +/- 128.\n", 
		iMinX, iMinY, iMaxX, iMaxY );
	Lprintf( &CConfig_c, sLog );
	return USER_ERROR_SIG;
      }
  }

  /* Check to see if we can find the sitename in the site mapping table.  Note
   * that wildcards are allowed.
   */
  nullify_ncpy( sSitename20, pheader_a->end.sing_sitename, 16 );
  pConvert_a->pSite = FindSiteByName( sSitename20, &CConfig_c );
  if (!pConvert_a->pSite)
    {
      sprintf( sLog, "ERROR, IRIS sitename '%s' not found in config site list\n", 
	       sSitename20 );
      Lprintf( &CConfig_c, sLog );
      return USER_ERROR_SIG;
    }

  /* Compute the scaling factor.  This is basically the log base 2
   * of the scale in nautical miles per pixel.
   */
  if (pheader_a->pcf.ixscale<1)
    {
      sprintf( sLog, "ERROR, product has invalid scale\n" );
      Lprintf( &CConfig_c, sLog );
      return USER_ERROR_SIG;
    }

  {
    FLT8 fScale, fOutScale, fDifference;
    SINT4 iScalingFactor;
    fScale = ((FLT8)pheader_a->pcf.ixscale)/(KM_PER_NM*100000.0);
    iScalingFactor = 6+NINT( log(fScale)/log(2.0) );
    if ((iScalingFactor<-16)||(iScalingFactor>15))
      {
	sprintf( sLog, "ERROR, Scale of %8.6f nm/pixel is out of range\n",
		 fScale );
	Lprintf( &CConfig_c, sLog );
	return USER_ERROR_SIG;
      }
    
    fOutScale = pow( 2.0, (FLT8)(iScalingFactor-6) );
    fDifference = fabs( fOutScale-fScale );
    sprintf( sLog, "Output Scale %8.6f nm/pixel, input %8.6f, %d%% error\n",
	     fOutScale, fScale, NINT( 100.0*fDifference/fScale) );
    Lprintf( &CConfig_c, sLog );
    pConvert_a->iScalingFactor = iScalingFactor;
  }

  /* Compute the time in UTC */
  UtcFromYmds( &pConvert_a->IngTimeUtc, &pheader_a->pcf.ing_time,
	       pheader_a->end.iMinutesWest );

  return SS_NORMAL;
}
/* =====================================================
 * Search to find the sitename.
 */
static const struct convert_site_table * FindSiteByName
( const char *sSitename16_a,             /* IRIS sitename, NULL terminated */
  const struct convert_config *pCConfig_a ) /* Configuration */
{
  SINT4 iSite;
  for (iSite=0; iSite<pCConfig_a->iSiteCount; iSite++)
    {
      const struct convert_site_table *pSite;
      pSite = &(pCConfig_a->Sites[iSite]);
      if (sig_fnmatch( pSite->sSitename16, sSitename16_a ))
	{
	  return pSite;
	}
    }
  return NULL;
}

/* ==============================================================
 * Convert product to Asterix.  Entered with a pointer to the beginning 
 * of the full product.  Should exit if it gets an error.
 */
static void ConvertToAsterix
(const struct cartesian_product *pProd_a,
 struct convert_info *pConvert_a )
{
  struct convert_state State;

  /* Allocate the data block buffer */
  memset( &State, 0, sizeof(State) );
  State.pBlock = (struct ast_data_block*)malloc( CConfig_c.iMaxBlockSize );
  if (!State.pBlock)
    {
      sig_signal(errno_to_message(errno));
      exit(EXIT_FAILURE);
    }

  /* Write the Start of Picture */
  WriteSOP( pConvert_a, &State );

  /* Write some data */
  ConvertProductData( pProd_a, pConvert_a, &State );

  /* Write the End of Picture */
  WriteEOP( pConvert_a, &State );

  FlushDataBlock( &State );

  return;
}

/* ================================================================
 * Convert the product data section.
 */
static void ConvertProductData
( const struct cartesian_product *pProd_a,
  const struct convert_info *pConvert_a,
  struct convert_state *pState_a )
{
  int iLine;
  UINT1 *pLevelBuffer;
  SINT4 iRes, iSize, iLevel;
  const UINT1 *pInBuffer;
  const struct product_hdr *pPHeader;
  struct cartesian_vectors Vectors[8];

  pInBuffer = pProd_a->Data;
  pPHeader = &(pProd_a->Header);
  iRes = idata_resolution( pPHeader->pcf.idata_out );

  /* Malloc the buffer to hold the levelized data */
  iSize=pPHeader->pcf.ixsize*pPHeader->pcf.iysize;
  pLevelBuffer = (UINT1*)malloc(iSize);
  if (!pLevelBuffer)
    {
      sig_signal(errno_to_message(errno));
      exit(EXIT_FAILURE);
    }
  levelize_8bit( pInBuffer, iSize, &(pConvert_a->Level), iRes, pLevelBuffer );

  /* Initialize the record headers */
  memset(Vectors, 0, 8*sizeof(struct cartesian_vectors) );
  for (iLevel=0; iLevel<8; iLevel++)
    {
      struct cartesian_vectors *pVector=&(Vectors[iLevel]);
      pVector->iFieldSpec  = AST_FRN1_DATA_SOURCE;
      pVector->iFieldSpec |= AST_FRN1_MESSAGE_TYPE;
      pVector->iFieldSpec |= AST_FRN1_VECTOR_QUALIFIER;
      pVector->iFieldSpec |= AST_FRN1_CARTESIAN_VECTORS;
      pVector->iSAC        = pConvert_a->pSite->iSAC;
      pVector->iSIC        = pConvert_a->pSite->iSIC;
      pVector->iMessageType = AST_MSG_CARTESIAN_VECTOR;
      pVector->iVectorQualifier = (iLevel<<4)|0x08;
    }

  /* Search through the whole array, converting to vectors and
   * accumulating the vectors in groups of each level.
   */

  /* Go from the top down */
  for (iLine=pPHeader->pcf.iysize-1; iLine>=0; iLine--)
    {
      const UINT1 *pLevelLine=pLevelBuffer+(iLine*pPHeader->pcf.ixsize);
      VectorizeLine( iLine, pLevelLine, pPHeader->pcf.ixsize, Vectors, 
		     pConvert_a, pState_a );
    }

  /* Write out the remaining vector buffers */
  for (iLevel=0; iLevel<8; iLevel++)
    {
      if (Vectors[iLevel].iRepeats>0) WriteVectors( &Vectors[iLevel], pState_a );
    }

}

/* =========================================================================
 * Convert a line of 3-bit data into Cartesian vectors.
 */
static void VectorizeLine
( SINT4 iYPos_a,           /* Signed Y-component position */
  const UINT1 *pBuffer_a,  /* Buffer of data */  
  SINT4 iXSize_a,          /* Number of pixels */
  struct cartesian_vectors Vectors_a[8],  /* Buffers holding vector info */
  const struct convert_info *pConvert_a,
  struct convert_state *pState_a )
{
  SINT4 iStartXPos, iXPos, iRunLength;
  UINT1 iLevel;

  /* Run length code the data */
  iStartXPos=0;
  iLevel = 0x07&pBuffer_a[iStartXPos];
  for (iXPos=1; iXPos<iXSize_a; iXPos++)
    {
      /* Look for value changes only */
      if (iLevel!= (0x07&pBuffer_a[iXPos]))
	{
	  /* Store the previous run */
	  iRunLength=iXPos-iStartXPos;
	  AddVector( iStartXPos-pConvert_a->iCenterX, iYPos_a-pConvert_a->iCenterY,
		     iRunLength, &(Vectors_a[iLevel]) );
	  if (Vectors_a[iLevel].iRepeats == 255)
	    WriteVectors( &Vectors_a[iLevel], pState_a );
	  /* Start the new run */
	  iStartXPos = iXPos;
	  iLevel = 0x07&pBuffer_a[iStartXPos];
	}
    }

  /* Write out the last run on the line */
  iRunLength = iXSize_a-iStartXPos;
  AddVector( iStartXPos-pConvert_a->iCenterX, iYPos_a-pConvert_a->iCenterY, 
	     iRunLength, &(Vectors_a[iLevel]) );
  if (Vectors_a[iLevel].iRepeats == 255) WriteVectors( &Vectors_a[iLevel], pState_a );

  return;

}
/* =============================================================
 * Add a vector to a sequence.
 */
static void AddVector
( SINT4 iXComp_a,
  SINT4 iYComp_a,
  SINT4 iRunLength_a,
  struct cartesian_vectors *pVectors_a )
{
  SINT4 iRepeats=(SINT4)(UINT4)pVectors_a->iRepeats;
  pVectors_a->Vector[iRepeats].iXComp = iXComp_a;
  pVectors_a->Vector[iRepeats].iYComp = iYComp_a;
  pVectors_a->Vector[iRepeats].iLength = iRunLength_a;
  pVectors_a->iRepeats++;

}
/* ===========================================================================
 * This subroutine computes the color map and the color parameter scale
 * text from the product header.
 */
static void ComputeColormap
(UINT2 idtype_a,                    /* Data type */
 const 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;
  struct color_scale_def ScaleDef;
  SINT4 iColor;
  UINT2 iColorScale[COLOR_MAP_ZOOM][16];
  UINT1 iColorNoData, iColorThresh;

  /* ----------------------------------------------------------------------- */

  FillDataConvert( &Convert, pend_a->iprf, pend_a->itrig, pend_a->ilambda, pend_a->ipolar,
		   CONVERT_FLAGS_DEFAULT ) ;
  
  memset( &ScaleDef, 0, sizeof(ScaleDef) );
  ScaleDef.iflags  = COLOR_SCALE_VARIABLE | pPrivateScale_a->iflags;
  ScaleDef.icolcnt = pPrivateScale_a->icolcnt;

  /* Color scale is replicated because we are not using zoomed scale */
  for (iColor=0; iColor<16; iColor++)
    {
      ScaleDef.ilevel_seams[iColor] =  pPrivateScale_a->ilevel_seams[iColor];
      iColorScale[0][iColor] = MIN( iColor+1, 15 );
      iColorScale[1][iColor] = MIN( iColor+1, 15 );
    }
  iColorNoData = 0;
  iColorThresh = 0;
  generic_map( idtype_a, &ScaleDef, iColorScale,
	       iColorThresh, iColorNoData, plevel_a, &Convert, newscale_a );

}
/* ===============================================
 * Write out a sequence of cartesian vectors.  Zero it afterwards.
 */
static void WriteVectors
( cartesian_vectors *pVectors_a, 
  struct convert_state *pState_a )
{
  SINT4 iSize, iRepeats;

  iRepeats = (SINT4)(UINT4)pVectors_a->iRepeats;
  iSize = 6+3*iRepeats;

  WriteOneRecord( (char*)pVectors_a, iSize, pState_a );
  pVectors_a->iRepeats=0;

  pState_a->iVectorCount+=iRepeats;

}
/* ============================================
 * Write the SOP Record
 */
static void WriteSOP
( const struct convert_info *pConvert_a,
  struct convert_state *pState_a )
{
  UINT4 iTime;
  char sBuffer[20];

  sBuffer[0]  = AST_FRN1_DATA_SOURCE;
  sBuffer[0] |= AST_FRN1_MESSAGE_TYPE;
  sBuffer[0] |= AST_FRN1_EXTENSION;

  sBuffer[1]  = AST_FRN2_TIME_OF_DAY;
  sBuffer[1] |= AST_FRN2_PROCESSING_STATUS;
  // sBuffer[1] |= AST_FRN2_STATION_CONFIG;

  /* Data Source */
  sBuffer[2]  = pConvert_a->pSite->iSAC;
  sBuffer[3]  = pConvert_a->pSite->iSIC;

  /* Message Type */
  sBuffer[4]  = AST_MSG_SOP;

  /* Time of Day in 1/128 of seconds */
  iTime  = 128*pConvert_a->IngTimeUtc.isec;
  iTime += (128*(YMDS_MASK_MS&pConvert_a->IngTimeUtc.imills))/1000;
  sBuffer[5]  = 0xff & (iTime>>16);
  sBuffer[6]  = 0xff & (iTime>>8);
  sBuffer[7]  = 0xff & iTime;

  /* Processing Status */
  sBuffer[8]  = 0xff & (pConvert_a->iScalingFactor<<3);
  sBuffer[9]  = 0;
  sBuffer[10] = 0;

  /* Station Configuration */
  //  sBuffer[11] = 0;

  /* Write data out */
  WriteOneRecord( sBuffer, 11, pState_a );

  return;
}

/* ============================================
 * Write the EOP Record
 */
static void WriteEOP
( const struct convert_info *pConvert_a,
  struct convert_state *pState_a )
{
  UINT4 iTime;
  char sBuffer[20];

  sBuffer[0]  = AST_FRN1_DATA_SOURCE;
  sBuffer[0] |= AST_FRN1_MESSAGE_TYPE;
  sBuffer[0] |= AST_FRN1_EXTENSION;

  sBuffer[1]  = AST_FRN2_TIME_OF_DAY;
  // sBuffer[1] |= AST_FRN2_STATION_CONFIG;
  sBuffer[1] |= AST_FRN2_TOTAL_NUMBER;

  /* Data Source */
  sBuffer[2]  = pConvert_a->pSite->iSAC;
  sBuffer[3]  = pConvert_a->pSite->iSIC;

  /* Message Type */
  sBuffer[4]  = AST_MSG_SOP;

  /* Time of Day in 1/128 of seconds */
  iTime  = 128*pConvert_a->IngTimeUtc.isec;
  iTime += (128*(YMDS_MASK_MS&pConvert_a->IngTimeUtc.imills))/1000;
  sBuffer[5]  = 0xff & (iTime>>16);
  sBuffer[6]  = 0xff & (iTime>>8);
  sBuffer[7]  = 0xff & iTime;

  /* Station Configuration */
  //  sBuffer[8]  = 0;

  /* Total Number */
  sBuffer[8]  = 0xff & (pState_a->iVectorCount>>8);
  sBuffer[9]  = 0xff & (pState_a->iVectorCount);

  /* Write data out */
  WriteOneRecord( sBuffer, 10, pState_a );

  return;
}

/* ==================================================
 * Write one record out.
 */
static void WriteOneRecord
( const char *sBuffer_a,
  SINT4 iSize_a,
  struct convert_state *pState_a )
{
  char sLog[160];

  /* Check that the record can fit in a block */
  if (iSize_a+3>CConfig_c.iMaxBlockSize)
    {
      sprintf( sLog, "ERROR: A %d byte record cannot fit in the max block size of %d\n",
	       iSize_a, CConfig_c.iMaxBlockSize );
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }

  /* Write out the data block if it will not fit the new data */
  if ((pState_a->iBlockSize+iSize_a+3)> CConfig_c.iMaxBlockSize)
    {
      FlushDataBlock( pState_a );
    }

  /* Add in the new record */
  memcpy( pState_a->pBlock->iData+pState_a->iBlockSize, sBuffer_a, iSize_a );
  pState_a->iBlockSize += iSize_a;

}

/* ======================================================================
 * Flush the data block.
 */
static void FlushDataBlock( struct convert_state *pState_a )
{
  char sLog[160];
  int iSize;

  pState_a->iBlockSize+=3;
  
  pState_a->pBlock->iCategory=8;
  pState_a->pBlock->iLength[0]= 0xff & (pState_a->iBlockSize>>8);
  pState_a->pBlock->iLength[1]= 0xff & (pState_a->iBlockSize);
  iSize = fwrite( pState_a->pBlock, 1, pState_a->iBlockSize, stdout);
  if ( iSize != pState_a->iBlockSize)
    {
      sprintf( sLog, "Errno %d in fwrite\n", errno );
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }
  
  pState_a->iBlockSize=0;
}
/* ======================================================================
 * Function to print signaled errors to the loging file or terminal.
 */
static MESSAGE SignalHandler
(MESSAGE iStatus_a,
 UINT4 iCount_a,
 const UINT4 iSigArgs_a[] )
{
  char sMessage[170];

  strncpy( sMessage, message_to_text( iStatus_a, iCount_a, iSigArgs_a ), 165 );
  sMessage[165]=0;
  strcat( sMessage, "\n" );

  Lprintf( &CConfig_c, sMessage );
  return(SS_NORMAL);
}
