/* *********************************************************
 * *                                                       *
 * *  Function to convert an BMP Image to ADIDS format     *
 * *                                                       *
 * *********************************************************
 * File: utils/adids/BMPToAdids.C
 *
 *                      COPYRIGHT (c) 2003  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 a BMP filename in IIA format
 * as 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 <termios.h>
#define INCLUDE_TERMIOS_PROTOTYPES 1

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

#include "SigAdids.h"

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

/* Local function prototypes
 */

static void ConvertProduct
(const char *prod_buf);

static void ConvertProductData
( const char *prod_buf);

static void RunLengthLine
( SINT4 iYPos_a,           /* Y-component position */
  const UINT1 *pBuffer_a,  /* Buffer of data */  
  SINT4 iXSize_a);         /* Number of pixels */

static void WriteSOP();

static void WriteEOP();

static void WriteOneRecord
( const struct adids_single_line *pBuffer_a,
  SINT4 iSize_a );

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

int iCommandCode;

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  char buf[600], *prod_buf;
  MESSAGE iStatus;
  SINT4 iCount, iWantCount ;
  char sLog[200];
  struct ttydev Tty;
  int i;
  FILE *infile, *outfile;

  setlocale( LC_ALL, "" ) ;

  /* Process the setup file, if any */
  LoadSetup( &CConfig_c, "BMPToAdids.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, "IrisToAdids.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, "IrisToAdids" );
    
  /* Check for no sites in the config file */
  if (CConfig_c.iProductCount<1)
    {
      Lprintf( &CConfig_c, "ERROR: No PRODUCT line in the BMPToAdids.conf file\n" );
      exit(EXIT_FAILURE);
    }

  sprintf(buf, "%s", sArgv[1]);
  infile = fopen(buf, "rb");
  if (!infile) 
    {
      sprintf( sLog, "ERROR: input file %s not found.\n", buf);
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }

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

  iWantCount = 4 * buf[46];
  iCount =  fread( buf, 1, iWantCount, infile );
  if (iCount != iWantCount) 
    {
      sprintf( sLog, "ERROR: Bad read, got%d, wanted %d\n", iCount, iWantCount);
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }

  /* Malloc input buffer */
  prod_buf  = (char *)malloc( 324 * 240 / 2 );
  if (!prod_buf)
    {
      sig_signal(errno_to_message(errno));
      exit(EXIT_FAILURE);
    }

  for (i=0;i<240;i++)
  {
    iWantCount = (324 / 2);
    iCount =  fread( &prod_buf[iWantCount*i], 1, iWantCount, infile );
    if (iCount != iWantCount)
      {
        sprintf( sLog, "ERROR: Bad read2, got%d, wanted %d\n", iCount, iWantCount);
        Lprintf( &CConfig_c, sLog );
        exit(EXIT_FAILURE);
      }
    iWantCount = 2;
    iCount =  fread( buf, 1, iWantCount, infile );
    if (iCount != iWantCount)
      {
        sprintf( sLog, "ERROR: Bad read2, got%d, wanted %d\n", iCount, iWantCount);
        Lprintf( &CConfig_c, sLog );
        exit(EXIT_FAILURE);
      }
  }
  
  /* Open output device, if applicable */
  if (CConfig_c.iOutputFormat==OUTPUT_SERIAL)
    {
      iStatus = openTTYDev( &Tty, &CConfig_c.SerialSpec );
      if (iStatus!=SS_NORMAL)
	{
	  sig_signal( iStatus );
	  exit(EXIT_FAILURE);
	}
      CConfig_c.pOutFile = Tty.Tx ;
    }
  else
    {
       
      outfile = fopen( "/tmp/BMPToAdids.dat", "wb" );
      if (!outfile)
	{
	  exit(EXIT_FAILURE);
	}
      CConfig_c.pOutFile = outfile;
    }

  iCommandCode = 0;
  for (i=0; i< CConfig_c.iProductCount; i++)
  {
    if (!strncmp (CConfig_c.Products[i].sPName, strrchr(sArgv[1], '/')+9, 13)) iCommandCode = CConfig_c.Products[i].iCommandCode;
      Lprintf( &CConfig_c, sLog );
  }
  if (!iCommandCode)
    {
      sprintf( sLog, "ERROR: No product match in table: %d %s\n", CConfig_c.iProductCount, strrchr(sArgv[1], '/')+9);
      Lprintf( &CConfig_c, sLog );
      exit(EXIT_FAILURE);
    }

  ConvertProduct( prod_buf );

  sprintf( sLog, "Converted  product\n"); 
  Lprintf( &CConfig_c, sLog );

  /* Close the output device, if applicable */
  if (CConfig_c.iOutputFormat==OUTPUT_SERIAL)
    {
      iStatus = closeTTYDev( &Tty );
      if (iStatus!=SS_NORMAL) sig_signal( iStatus );
    }
  
  exit( EXIT_SUCCESS );
}

/* =======================================================================
 * Check for some consistency. 
 */

/* ==============================================================
 * Convert product to ADIDS.  Entered with a pointer to the beginning 
 * of the full product.  Should exit if it gets an error.
 */
static void ConvertProduct
(const char *prod_buf)
{
  /* Write the Start of Picture */
  WriteSOP( );

  /* Write the data */
  ConvertProductData( prod_buf );

  /* Write the End of Picture */
  WriteEOP( );

  return;
}

/* ================================================================
 * Convert the product data section.
 */
static void ConvertProductData
( const char *prod_buf )
{
  int iLine, i, j;
  unsigned char buf_char, pLevelLine[324];

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

  /* Go from the top down */
  for (iLine=239; iLine>=0; iLine--)
    {
      j = 0;
      for ( i=0; i < (324/2); i++)
      {
        buf_char = prod_buf[ (iLine * 324 / 2) + i ];
        pLevelLine[j] = buf_char / 16;  
        pLevelLine[j+1] = buf_char % 16;  
        j += 2;
      }
      RunLengthLine( iLine, pLevelLine, 324 );
    }
}

/* =========================================================================
 * Convert a line of 4-bit data into run length coding.
 */
static void RunLengthLine
( SINT4 iYPos_a,           /* Y-component position */
  const UINT1 *pBuffer_a,  /* Buffer of data */  
  SINT4 iXSize_a )         /* Number of pixels */
{
  SINT4 iStartXPos, iXPos, iRunLength, iOutPos;
  UINT1 iLevel;
  struct adids_single_line Buffer;
  UINT2 iActionWord;

  /* Run length code the data */
  iStartXPos=0; iOutPos=0;
  iLevel = 0x0f&pBuffer_a[iStartXPos];
  for (iXPos=1; iXPos<iXSize_a; iXPos++)
    {
      iRunLength=iXPos-iStartXPos;
      /* Look for value changes, or maximum run length */
      if ((iLevel!= (0x0f&pBuffer_a[iXPos])) ||(iRunLength>=252))
      {
	/* Store the previous run */
	Buffer.Codes[iOutPos].iPixelValue=iLevel;
	Buffer.Codes[iOutPos++].iRepeats=iRunLength;
	
	/* Start the new run */
	iStartXPos = iXPos;
	iLevel = 0x0f&pBuffer_a[iStartXPos];
      }
    }
  /* Store the last run */
  iRunLength=iXSize_a-iStartXPos;
  Buffer.Codes[iOutPos].iPixelValue=iLevel;
  Buffer.Codes[iOutPos++].iRepeats=iRunLength;
  
  /* Add the header */
  /* sync word */
  Buffer.iSyncLow  = 0xfd;
  Buffer.iSyncHigh = 0xfe;

  /* Action word */
  iActionWord = (iOutPos+1)<<6;
  iActionWord |= iCommandCode;
  Buffer.iActionLow  = 0xff&iActionWord;
  Buffer.iActionHigh = iActionWord>>8;

  /* Line number */
  Buffer.iLineLow  = 0xff&iYPos_a;
  Buffer.iLineHigh = iYPos_a>>8;

  /* Write data out */
  WriteOneRecord( &Buffer, 2*(3+iOutPos) );

  return;
}

/* ============================================
 * Write the SOP Record
 */
static void WriteSOP
( )
{
  struct adids_single_line Buffer;
  UINT2 iActionWord;

  /* Sync word */
  Buffer.iSyncLow  = 0xfd;
  Buffer.iSyncHigh = 0xfe;

  /* Action word */
  iActionWord  = 1<<6;
  iActionWord |= iCommandCode;
  Buffer.iActionLow  = 0xff&iActionWord;
  Buffer.iActionHigh = iActionWord>>8;

  /* Message Type */
  Buffer.iLineLow  = 0;
  Buffer.iLineHigh = 0x81;

  /* Write data out */
  WriteOneRecord( &Buffer, 6 );

  return;
}

/* ============================================
 * Write the EOP Record
 */
static void WriteEOP
( )
{
  struct adids_single_line Buffer;
  UINT2 iActionWord;

  /* Sync word */
  Buffer.iSyncLow  = 0xfd;
  Buffer.iSyncHigh = 0xfe;

  /* Action word */
  iActionWord  = 1<<6;
  iActionWord |= iCommandCode;
  Buffer.iActionLow  = 0xff&iActionWord;
  Buffer.iActionHigh = iActionWord>>8;

  /* Message Type */
  Buffer.iLineLow  = 0;
  Buffer.iLineHigh = 0x8f;

  /* Write data out */
  WriteOneRecord( &Buffer, 6 );

  return;
}

/* ==================================================
 * Write one record out.
 */
static void WriteOneRecord
( const struct adids_single_line *Buffer_a,
  SINT4 iSize_a )
{
  int iError = fwrite( Buffer_a, 1, iSize_a, CConfig_c.pOutFile );
  if (iError!=iSize_a)
    {
      perror( "fwrite" );
      exit(EXIT_FAILURE);
    }
}

/* ======================================================================
 * 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);
}
