/* **********************************
 * *                                *
 * *  ADIDS format viewer program   *
 * *                                *
 * **********************************
 * File: utils/adids/AdidsView.C
 *
 *                   COPYRIGHT (c) 2003, 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 takes the filename of an ADIDS file and prints out diagnostic
 * information.
 */

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

#include "sigtypes.h"
#include "SigAdids.h"

#include "user_lib.h"

struct data_line
{
  UINT1 iData[ADIDS_MAX_WIDTH+10];
};
struct read_state
{
  struct data_line Lines[ADIDS_MAX_HEIGHT+10];  /* Array to store the data in */
  UINT2 iMaxX;
  UINT2 iMaxY;
  SINT4 iRecordCount;
  UINT2 iLineNumber;
  UINT2 iColumnNumber;
  UINT1 lStartFound;
  UINT1 lExtraStartFound;
  UINT1 lStartNotFirst;
  UINT1 lEndFound;
  UINT1 lExtraEndFound;
  UINT1 lLastWasEnd;   /* Last record was an end code */
};

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

static void Usage( void );

static SINT4 ReadDataLine
( struct adids_single_line *pLine_a,
  FILE *pStream_a );

static SINT4 ProcessDataLine
( UINT2 iCodeCount_a,   /* Code count, already checked for max size */
  const struct adids_code Codes_a[],
  struct read_state *pState_a,
  const struct view_options *pOptions_a );

static UINT1 InsertRunCode
( const struct adids_code *pCode_a,
  struct read_state *pState_a );

static void InsertCartesianPoint
( UINT1 iIntensity_a,      /* Only low 4 bits non-zero */
  struct read_state *pState_a );

static void PrintCartesianArray
( const struct read_state *pState_a,
  const struct view_options *pOptions_a );

struct view_options
{
  char sPath[PATHNAME_SIZE];
  UINT1 lVerbose;      /* Print block summary */
  UINT1 lVerbose2;     /* Print record summary */
  UINT1 lVerbose3;     /* Print record details */
  UINT1 lVerbose4;     /* Print individual vectors */
  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 iLineSize;
  struct view_options Options;
  FILE *stream;
  struct adids_single_line Line;
  struct read_state State;

  setlocale( LC_ALL, "" ) ;

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

  /* Initialize the read state */
  memset( &State, 0, sizeof(State) );

  /* Open the file */
  stream = fopen( Options.sPath, "r" );

  /* Loop reading in line, and displaying the results */
  iLineSize = ReadDataLine( &Line, stream );
  while( iLineSize )
    {
      State.iRecordCount++;
      State.iLineNumber  = Line.iLineLow;
      State.iLineNumber |= (Line.iLineHigh<<8);
      /* I assume all lines with the MSB on are special codes */
      if (State.iLineNumber&0x8000)
	{
	  switch(State.iLineNumber)
	    {
	    case 0x8100:
	      if (Options.lVerbose) 
		printf( "Record #%d: contains start code, size:%d bytes\n", 
			State.iRecordCount, iLineSize );
	      if (State.lStartFound) State.lExtraStartFound=TRUE;
	      if (State.iRecordCount!=1) State.lStartNotFirst=TRUE;
	      State.lStartFound=TRUE;
	      State.lLastWasEnd=FALSE;
	      break;
	    case 0x8f00:
	      if (Options.lVerbose) 
		printf( "Record #%d: contains end code, size:%d bytes\n", 
			State.iRecordCount, iLineSize );
	      if (State.lEndFound) State.lExtraEndFound=TRUE;
	      State.lEndFound=TRUE;
	      State.lLastWasEnd=TRUE;
	      break;
	    default:
	      printf( "Record #%d: contains invalid code:0x%x, size:%d bytes\n", 
			State.iRecordCount, State.iLineNumber, iLineSize );
	      State.lLastWasEnd=FALSE;
	      break;
	    }
	}
      else
	{
	  UINT2 iCodeCount=(iLineSize-6)/2;
	  if (Options.lVerbose) 
	    printf( "Record #%d: contains line #%d, size:%d bytes\n", 
		    State.iRecordCount, State.iLineNumber, iLineSize );
	  ProcessDataLine( iCodeCount, Line.Codes, &State, &Options );
	  State.lLastWasEnd=FALSE;
	}
      iLineSize = ReadDataLine( &Line, stream );
    }

  fclose( stream );

  /* Print errors releated to special codes */
  if (!State.lStartFound) fprintf( stdout, "ERROR: No start code found.\n" );
  if (State.lExtraStartFound) fprintf( stdout, "ERROR: Extra start codes found.\n" );
  if (State.lStartNotFirst) fprintf( stdout, "ERROR: Start code not first record.\n" );

  if (!State.lEndFound) fprintf( stdout, "ERROR: No end code found.\n" );
  if (State.lExtraEndFound) fprintf( stdout, "ERROR: Extra end codes found.\n" );
  if (!State.lLastWasEnd) fprintf( stdout, "ERROR: End code not last record.\n" );

  /* Now loop printing out the results */
  PrintCartesianArray( &State, &Options );
  
  exit( EXIT_SUCCESS );
}
/* =================================================================
 * Read a transmission of data from the stream.  On error, print out 
 * message and return a zero size.
 */
static SINT4 ReadDataLine
( struct adids_single_line *pLine_a,
  FILE *pStream_a )
{
  SINT4 iCount, iReadSize=0, iSkipCount=0, iWord;
  UINT2 iLineSize, iActionWord, *pShort;
  UINT1 lByteSwapped;
  
 FIRST_READ:
  iCount = fread( &(pLine_a->iSyncLow), 1, 1, pStream_a );
  if (iCount!=1)
    {
      /* Zero means the end of file */
      if (iCount!=0) fprintf( stdout, "fread returned %d\n", iCount );
      goto DONE;
    }
  
 FIRST_CHECK:
  if (pLine_a->iSyncLow!=0xfd)
    {
      iSkipCount++;
      goto FIRST_READ;
    }
  
  iCount = fread( &(pLine_a->iSyncHigh), 1, 1, pStream_a );
  if (iCount!=1)
    {
      /* Zero means the end of file */
      if (iCount!=0) fprintf( stdout, "fread returned %d\n", iCount );
      goto DONE;
    }
  
  if (pLine_a->iSyncHigh!=0xfe)
    {
      iSkipCount++;
      pLine_a->iSyncLow = pLine_a->iSyncHigh;
      goto FIRST_CHECK;
    }
  
  /* The sync word was found.  Test to see if the low byte is first. */
  lByteSwapped = (0xfefd != *(UINT2*)&(pLine_a->iSyncLow));
  
  iCount = fread( &(pLine_a->iActionLow), 1, 2, pStream_a );
  if (iCount!=2)
    {
      /* Zero means the end of file */
      if (iCount!=0) fprintf( stdout, "fread returned %d\n", iCount );
      goto DONE;
    }
  
  if (lByteSwapped) SwapU2( (UINT2*)&(pLine_a->iActionLow) );
  iActionWord = pLine_a->iActionLow;
  iActionWord |= (pLine_a->iActionHigh<<8);
  iLineSize = iActionWord>>6;
  if (iLineSize>(ADIDS_MAX_WIDTH+1))
    {
      fprintf( stdout, "Word count %d to big.\n", iLineSize );
      goto DONE;
    }
  iCount = fread( &(pLine_a->iLineLow), 1, 2*iLineSize, pStream_a );
  if (iCount!=2*iLineSize)
    {
      fprintf( stdout, "fread returned %d\n", iCount );
      goto DONE;
    }
  if (lByteSwapped) SwapU2Array( (UINT2*)&(pLine_a->iLineLow), iLineSize );
  
  /* Check for imbedded sync words */
  pShort = (UINT2*)&(pLine_a->iLineLow);
  for (iWord=0; iWord<iLineSize; iWord++)
    {
      if (0xfefd == pShort[iWord])
	fprintf( stdout, "Imbedded sync word in code word %d\n", iWord );
    }
  iReadSize = 4+(2*iLineSize);
  
 DONE:
  if (iSkipCount!=0) fprintf( stdout, "Skipped %d bytes looking for sync\n",
			      iSkipCount );
  return iReadSize;
  
}

/* ========================================================
 * Function to process a single data record.  It will return
 * zero on success, else the number of bytes read before the
 * overflow.  Note that only cartesian vector records are
 * processed for further display.
 */
static SINT4 ProcessDataLine
( UINT2 iCodeCount_a,   /* Code count, already checked for max size */
  const struct adids_code Codes_a[],
  struct read_state *pState_a,
  const struct view_options *pOptions_a )
{
  UINT1 lError;
  UINT2 iCode;

  /* Check the line number */
  if (pState_a->iLineNumber >= ADIDS_MAX_HEIGHT)
    {
      fprintf( stdout, "ERROR: Line number %d exceeds max of %d\n",
	       pState_a->iLineNumber, ADIDS_MAX_HEIGHT-1 );
      return 0;
    }

  /* Store max line number */
  if (pState_a->iLineNumber>pState_a->iMaxY) 
    pState_a->iMaxY=pState_a->iLineNumber;


  pState_a->iColumnNumber=0;
  lError = FALSE;
  iCode = 0;
  while( (!lError) && (iCode<iCodeCount_a))
    {
      if (pOptions_a->lVerbose4)
	printf( "Code #%d, Value:%d, Repeats:%d\n", iCode, 
		Codes_a[iCode].iPixelValue, Codes_a[iCode].iRepeats );
      lError = InsertRunCode( &(Codes_a[iCode++]), pState_a );
    }

  if (lError)
    return iCode;
  else
    return 0;
}
/* =====================================================================
 * Insert a cartesian vector into the array.  Returns TRUE if there
 * is an overrun error.
 */
static UINT1 InsertRunCode
( const struct adids_code *pCode_a,
  struct read_state *pState_a )
{
  UINT2 iCount;
  /* Only the low 4 bits of the value are used */
  UINT1 iIntensity = 0x0f & (pCode_a->iPixelValue);

  for (iCount=0; iCount<pCode_a->iRepeats; iCount++)
    {
      if (pState_a->iColumnNumber>=ADIDS_MAX_WIDTH) return TRUE;
      InsertCartesianPoint( iIntensity, pState_a );
      pState_a->iColumnNumber++;
    }

  /* Store limits of data span */
  if (pState_a->iColumnNumber>0)
    {
      if ((pState_a->iColumnNumber-1)>pState_a->iMaxX) 
	pState_a->iMaxX=pState_a->iColumnNumber-1;
    }

  return FALSE;
}
/* ===================================================================
 * Insert one point into the array.  Position is known to be valid.
 */
static void InsertCartesianPoint
( UINT1 iIntensity_a,      /* Only low 4 bits non-zero */
  struct read_state *pState_a )
{
  UINT1 *pData=&pState_a->Lines[pState_a->iLineNumber].iData[pState_a->iColumnNumber];
  /* In the data array, each byte contains an insertion count
   * in the top 4 bits, and the last data value in the low 4 bits
   */
  UINT1 iInsertCount = (*pData)>>4;

  if (iInsertCount<15) iInsertCount++;
  *pData = (iInsertCount<<4)|iIntensity_a;

}
/* ===================================================================
 * Function to print out numbers from the array.
 */
static void PrintCartesianArray
( const struct read_state *pState_a,
  const struct view_options *pOptions_a )
{
  SINT4 iArrayWidth, iArrayHeight, iSkipFactor;
  SINT4 iLine;

  /* If the width of the image will not fit on one line, the use
   * a skip factor.
   */
  if (pState_a->iMaxX<=0) return;
  iArrayWidth = pState_a->iMaxX+1;
  iArrayHeight = pState_a->iMaxY+1;

  iSkipFactor = (iArrayWidth+pOptions_a->iLineLength-1)/pOptions_a->iLineLength;

  printf( "Legend: HEX values; .=no value; Lower case letters=duplicates\n");
  printf( "Array size (%dx%d), display skip factor:%d\n", iArrayWidth, iArrayHeight,
	  iSkipFactor );
  for (iLine=iArrayHeight-1; iLine>=0; iLine-=iSkipFactor )
    {
      SINT4 iX;
      for (iX=0; iX<iArrayWidth; iX+=iSkipFactor )
	{
	  UINT1 iValue, iCount, iLevel;
	  iValue=pState_a->Lines[iLine].iData[iX];
	  iLevel = 0x0f&iValue; iCount=iValue>>4;
	  switch( iCount )
	    {
	    case 0:
	      printf(".");  /* dot for no value */
	      break;
	    case 1:
	      printf("%X",iLevel); /* Upper case hex for value */
	      break;
	    default:
	      printf("%c", 96+iCount); /* Lower case letter for duplicates */
	      break;
	    }
	}
      printf("\n");

    }

}
/* ======================================================================
 * 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( stdout, "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 == strcmp( sArg, "-v" ))
	    {
	      pOptions_a->lVerbose=TRUE;
	    }
	  else if (0 == strcmp( sArg, "-vv" ))
	    {
	      pOptions_a->lVerbose=TRUE;
	      pOptions_a->lVerbose2=TRUE;
	    }
	  else if (0 == strcmp( sArg, "-vvv" ))
	    {
	      pOptions_a->lVerbose=TRUE;
	      pOptions_a->lVerbose2=TRUE;
	      pOptions_a->lVerbose3=TRUE;
	    }
	  else if (0 == strcmp( sArg, "-vvvv" ))
	    {
	      pOptions_a->lVerbose=TRUE;
	      pOptions_a->lVerbose2=TRUE;
	      pOptions_a->lVerbose3=TRUE;
	      pOptions_a->lVerbose4=TRUE;
	    }
	}
      else
	{
	  strncpy( pOptions_a->sPath, sArg, PATHNAME_SIZE-1);
	}
    }
  if (!pOptions_a->sPath[0])
    {
      printf( "ufview requires a pathname arg.\n" );
      exit(EXIT_FAILURE);
    }
  return;
}
/* ==================================================
 */
static void Usage( void )
{
  fprintf( stdout, "Options: \n" );
  fprintf( stdout, "         -help     (print this list and exit)\n" );
  fprintf( stdout, "         -length:N (max line length to use)\n" );
  fprintf( stdout, "         -v        (print summary for each data block)\n" );
  fprintf( stdout, "         -vv       (print summary for each record)\n" );
  fprintf( stdout, "         -vvv      (print details for each record)\n" );
  fprintf( stdout, "         -vvvv     (print details for each vector)\n" );
  fprintf( stdout, "         pathname\n" );
  fprintf( stdout, "         (all other arguments ignored)\n" );
}
