/*********************************************************************
**********************************************************************
*                                                                    *
*                        C O P Y R I G H T                           *
*                                                                    *
*                    Copyright 1988,1989,1990 (C) by                 *
*                    Purdue Research Foundation                      *
*                    Purdue University                               *
*                    West Lafayette, Indiana 47907                   *
*                                                                    *
* This software,  in whole or in part,  may be used  and copied only *
* with the written permission of the  Dept. of Earth and Atmospheric *
* Sciences  via  E.  M. Agee,  Purdue  University,  West  Lafayette, *
* Indiana,  and with the  inclusion  of the above  copyright notice. *
* This  software  or  any  copies  thereof  may  not be  provided or *
* otherwise made  available  to any  other person.  No title  to and *
* ownership of the software is hereby transferred.                   *
*                                                                    *
**********************************************************************

   ROUTINE: sat_read.c
   PROGRAMMER: Dan Vietor
   PROGRAM TYPE: WXP_LIBRARY
   VERSION: 1.3
   WXP VERSION: 4.8
   DATE: 930915

   COMPUTER:
      IBM RS/6000 running AIX 3.1 C Compiler
      IBM RT/PC running AIX 2.2.1 C Compiler
      IBM RT/PC running BSD 4.3 C Compiler
      IBM AT compatible running MSDOS 3.x using the
         Microsoft C 6.0 Compiler.
      SUN 3 and 4 running SUNOS 4.1
      DEC VaxStation running VMS 5.3 and GKS
      Interprocess communications based on either ATT UNIX System V
         Message queues or BSD Sockets.
      Asynchronous data stream function interface based on either
         System V ioctl, BSD 4.3 ioctl or Blaise Asynch Manager
         for MSDOS 3.x.
      Graphics calls based on ANSI C language Graphical Kernel
         System binding (revision dated July 1988)

   DESCRIPTION: This subroutine reads McIDAS area files and passes
      a data structure back with the image and navigation 
      information.

   REVISIONS:                                    DATE:       INIT:
Version 1                                        920115      DEV
Added navigation info                            920515      DEV  1.1
Debugged read in routines                        921015      DEV  1.2
Debugged read in routines,
Added SAT_read interface for generic images      930915      DEV  1.3

***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "wxp.h"
#include "mc_area.h"

static FILE *ifile;        /* Input file */
static unsigned char data[4000]; /* Input data from file */
static unsigned char scan_line[1600]; /* Current scan line */
static int dat_ind;        /* Index into data array */
static int dat_buf;        /* Current number of bytes in data array */
static int scan_ind;       /* Index to current scan line */
static int nib_bytes;      /* Current number of 4 bit nibbles read */
static int low = 0;        /* Specifies which 4 bit nibble to read */

/****************************************************************
   GETWORD - Gets a 4 byte word from file
****************************************************************/
unsigned int getword()

{
unsigned int output;

if( dat_ind >= dat_buf ){
   dat_buf = fread( data,1,4000,ifile );
   dat_ind = 0;
   }
output = *(int *)(data+dat_ind);
dat_ind += 4;
return output;
}

/****************************************************************
   GETBYTE - Gets a single byte from file
****************************************************************/
int getbyte()

{
if( dat_ind >= dat_buf ){
   dat_buf = fread( data,1,4000,ifile );
   if( dat_buf == -1 ) return -1;
   dat_ind = 0;
   }
return (int)data[dat_ind++];
}

/****************************************************************
   GETNIB - Gets a 4 bit nibble from file
****************************************************************/
int getnib()

{
if( dat_ind >= dat_buf ){
   dat_buf = fread( data,1,4000,ifile );
   if( dat_buf == -1 ) return -1;
   dat_ind = 0;
   }
if( !low ){
   low = 1;
   return data[dat_ind] >> 4;
   }
else {
   low = 0;
   nib_bytes++;
   return data[dat_ind++] & 0xF;
   }
}

/****************************************************************
   GETSYNC - Reads in the synch code from file
****************************************************************/
getsync()

{
int number;
int err;

number = 0;
while( number < 1024 ){
  number++;
  if(( err=getbyte()) == -1 ) return -1;
  if( err != 0x00 ) continue;
  if( getbyte() != 0xFF ) continue;
  if( getbyte() != 0x00 ) continue;
  if( getbyte() != 0xFF ) continue;
  if( getbyte() != 0x00 ) continue;
  if( getbyte() != 0xFF ) continue;
  if( getbyte() != 0x00 ) continue;
  if( getbyte() != 0xFF ) continue;
  return 1;
  }
return 0;
}

/****************************************************************
   SWAPWORD - swaps the bytes in a word
****************************************************************/
void swapword( ptr )

unsigned char *ptr;

{
unsigned char ch;

ch = *ptr;
*ptr = *(ptr+3);
*(ptr+3) = ch;
ch = *(ptr+1);
*(ptr+1) = *(ptr+2);
*(ptr+2) = ch;
}

/****************************************************************
   AREA_READ - Read in the area from file, determines whether
   packed or unpacked and performs unpacking.
****************************************************************/
int AREA_read( file, area )

FILE *file;
struct mc_area **area;    /* Area data structure */

{
int i,j,k;                 /* Loop control variables */
int mode;                /* Mode of image in bits */
int size;                /* Size of packed line in nibbles */
int pre_scan;            /* Previous scan line */
int scan;                /* Current scan line in image */
int width;               /* Width of image */
int memwidth;            /* Width of image memory*/
int height;              /* Height of image */
int depth;               /* data element size */
int file_type;           /* File type */
int cal;                 /* calibration block location */
int datloc;              /* data block location */
int status;              /* Function status */
unsigned int temp;       /* Temporary integer variable */
unsigned int ch;         /* Bytes read in from file */
unsigned int ref;        /* Previous pixel value */
unsigned char *ptr;      /* Pointer into structures */
unsigned char *glddat;   /* Pointer to bytes in Gould format */
unsigned long swaptest = 1; /* Used for determination of byte order */

double GouldToNative (unsigned char *);

swaptest = *(char *)&swaptest;
/*
   Set the file pointer
*/
ifile = file;
if( ifile == NULL )
   return -1;
rewind( file );
/*
   Allocate the area data structures
*/
*area = (struct mc_area *)malloc(sizeof(struct mc_area));

dat_ind = dat_buf = 0;
/*
   Determine file type
   if 4 or 0x4000000 => unpacked
   else              => packed
*/
temp = getword();
temp = getword();
if( temp == 4 )
   file_type = 1;
else if( temp == 0x4000000 )
   file_type = 2;
else
   file_type = 0;
dat_ind = 0;
/*
   If packed, search for sync bytes
*/
if( !file_type && getsync() != 1 ) return -1;
/*
  Read in area directory
*/
(*area)->dir = (struct area_dir *)malloc(sizeof(struct area_dir));
memcpy( (*area)->dir,data+dat_ind,sizeof(struct area_dir));
if(( file_type == 0 && swaptest ) || file_type == 2 ){
   ptr = (unsigned char *)(*area)->dir;
   for( i = 0; i < 24; i++,ptr+=4 )
      swapword( ptr );
   ptr = (unsigned char *)(*area)->dir+32*4;
   for( i = 32; i < 64; i++,ptr+=4 )
      swapword( ptr );
   }
if( !file_type && getsync() != 1 ) return -1;
/*
   Read in area navigation
*/
dat_ind = (*area)->dir->navoffst;
if( !strncmp( (const char*)data+dat_ind,"GOES",4 )){
   printf(" Nav data is GOES\n");
   strcpy( (*area)->type,"GOES" );
   (*area)->navg = (struct nav_goes *)malloc(sizeof(struct nav_goes));
   memcpy( (*area)->navg,data+dat_ind,sizeof(struct nav_goes));
   if(( file_type == 0 && swaptest ) || file_type == 2 ){
      ptr = (unsigned char *)(*area)->navg + 4;
      for( i = 1; i < 128; i++,ptr+=4 )
         swapword( ptr );
      }
   dat_ind+=512;
   }
else if( !strncmp( (const char *)data+dat_ind,"GVAR",4 )){
   printf(" Nav data is GVAR\n");
   strcpy( (*area)->type,"GVAR" );
   (*area)->navgv = (struct nav_gvar *)malloc(sizeof(struct nav_gvar));
   memcpy( (*area)->navgv,data+dat_ind,sizeof(struct nav_gvar));
   if(( file_type == 0 && swaptest ) || file_type == 2 ){
      ptr = (unsigned char *)(*area)->navgv + 4;
      for( i = 1; i < 640; i++,ptr+=4 )
         swapword( ptr );
      }
   dat_ind+=2560;
   }

/* Read calibration data, if any */

if( (*area)->dir->caloffst != 0 ) {

/*
   Allocate calibration data
*/
    (*area)->calgv =
          (struct cal_gvar *)malloc(sizeof(struct cal_gvar));
/* 
   Position file pointer to the beginning of the calibration block
*/
    cal = (*area)->dir->caloffst;
    status = fseek( ifile, cal, 0);
/*
   Allocate dummy byte data array for reading Gould-formatted reals
   as a sequence of bytes.
*/

    glddat = (unsigned char *)(malloc(sizeof(struct cal_gvar)));

/*
   Read calibration data
*/

    printf("Reading and converting calibration data");
    fread( glddat, 1, sizeof(struct cal_gvar), ifile );

/*
   Translate Gould floating point numbers to IEEE
   A Gould floating-point number is 4-bytes long, hence the steps
   by four.

*/
    for ( i = 0; i < 8 ; i++ ) {
        k=i*4;
        ((*area)->calgv->visbias[i])=(float)GouldToNative(glddat+k);
        printf("visbias[%d]=%10.4f\n",i,(*area)->calgv->visbias[i]);
        }

    for ( i = 0; i < 8 ; i++ ) {
        k=32+i*4;
        (*area)->calgv->vis1gain[i]=(float)GouldToNative(glddat+k);
        printf("vis1gain[%d]=%10.4f\n",i,(*area)->calgv->vis1gain[i]);
        }

    for ( i = 0; i < 8 ; i++ ) {
        k=64+i*4;
        (*area)->calgv->vis2gain[i]=(float)GouldToNative(glddat+k);
        printf("vis2gain[%d]=%10.4f\n",i,(*area)->calgv->vis2gain[i]);
         }

    (*area)->calgv->albedcon=(float)GouldToNative(glddat+96);
    printf("albedo constant=%10.4f\n",(*area)->calgv->albedcon);

    for ( i = 0; i < 4 ; i++ ) {
        k=100+i*4;
        (*area)->calgv->ir1bias[i]=(float)GouldToNative(glddat+k);
        printf("ir1bias[%d]=%10.4f\n",i,(*area)->calgv->ir1bias[i]);
        }

    for ( i = 0; i < 4 ; i++ ) {
        k=116+i*4;
        (*area)->calgv->ir2bias[i]=(float)GouldToNative(glddat+k);
        printf("ir2bias[%d]=%10.4f\n",i,(*area)->calgv->ir2bias[i]);
        }

    for ( i = 0; i < 4 ; i++ ) {
        k=132+i*4;
        (*area)->calgv->ir1gain[i]=(float)GouldToNative(glddat+k);
        printf("ir1gain[%d]=%10.4f\n",i,(*area)->calgv->ir1gain[i]);
        }

    for ( i = 0; i < 4 ; i++ ) {
        k=148+i*4;
        (*area)->calgv->ir2gain[i]=(float)GouldToNative(glddat+k);
        printf("ir2gain[%d]=%10.4f\n",i,(*area)->calgv->ir2gain[i]);
        }

    for ( i = 0; i < 87; i++ ) {
        (*area)->calgv->calresvd[i]=0.;
        }

/*
    Free-up the temporary memory used for reading.
*/

    free (glddat);
    
    }

width = (*area)->dir->esiz;
height = (*area)->dir->lsiz;
depth = (*area)->dir->zsiz;
memwidth = (*area)->dir->yzprefix+(width*depth);

if( width <= 0 || width > 100000 ) return -1;
if( height <= 0 || height > 100000 ) return -1;
/*
   Allocate image data
*/

ptr = (*area)->image =
   (unsigned char *)malloc( memwidth * height );

/* 
   Position file pointer to the beginning of the data block
*/

datloc = (*area)->dir->datoffst;
status = fseek( ifile, datloc, 0);

/*
   If unpacked, just read in
*/

if( file_type != 0 ){
   fread( ptr, 1, (memwidth*height), ifile );
   }
/*
   Unpack data
*/
else {
   scan = 0;
   for( j = 0; j < height; j++ ){
      for( i = 0; i < width; i++ )
         scan_line[i] = 0;
      pre_scan = scan;
/*
   Get the number of the next scan line in the image
*/
      if( getsync() == 1 ){
         scan = (unsigned long)getbyte() << 24;
         scan |= (unsigned long)getbyte() << 16;
         scan |= (unsigned long)getbyte() << 8;
         scan |= (unsigned long)getbyte();
         if(( pre_scan > 0 && scan > pre_scan + 10 ) ||
              scan > height || scan < pre_scan ) scan = pre_scan;
         }
      else 
         scan = height;
/*
   Fill image array for missing scan lines
*/
      for( i = pre_scan; i < scan-1 && j < height; i++ ){
         memcpy( ptr, scan_line, width );
         ptr += width;
         j++;
         }
     if( scan == height ) continue;
/*
   Find out how many nibbles in line and mode of data
*/
      mode = (unsigned long)getbyte() << 8;
      mode |= getbyte();
      size = (unsigned long)getbyte() << 8;
      size |= getbyte();
      low = 0;
      scan_ind = 0;
      ref = 0;
/*
   Read through each nibble
*/
      for( nib_bytes = 0; nib_bytes < size; nib_bytes++) {
         ch = getnib();
         if( ch == 0xFF || scan_ind >= width ) break;
         switch( ch ){
/*
   New value case, read in next 8 bits
*/
            case 0x0:
               ref = getnib() << 4;
               ref |= getnib();
               scan_line[scan_ind++] = ref; break;
/*
   Other cases, differences from previous pixel value
*/
            case 0x1: ref = scan_line[scan_ind++] = ref - 6; break;
            case 0x2: ref = scan_line[scan_ind++] = ref + 6; break;
            case 0x3: ref = scan_line[scan_ind++] = ref - 5; break;
            case 0x4: ref = scan_line[scan_ind++] = ref + 5; break;
            case 0x5: ref = scan_line[scan_ind++] = ref - 4; break;
            case 0x6: ref = scan_line[scan_ind++] = ref + 4; break;
            case 0x7: ref = scan_line[scan_ind++] = ref - 3; break;
            case 0x8: ref = scan_line[scan_ind++] = ref + 3; break;
            case 0x9: ref = scan_line[scan_ind++] = ref - 2; break;
            case 0xA: ref = scan_line[scan_ind++] = ref + 2; break;
            case 0xB: ref = scan_line[scan_ind++] = ref - 1; break;
            case 0xC: ref = scan_line[scan_ind++] = ref + 1; break;
            case 0xD: scan_line[scan_ind++] = ref; break;
            case 0xE:
               scan_line[scan_ind++] = ref;
               scan_line[scan_ind++] = ref; break;
            case 0xF:
               scan_line[scan_ind++] = ref;
               scan_line[scan_ind++] = ref;
               scan_line[scan_ind++] = ref; break;
            }
         }
/*
   If 6 bit mode, shift to 8 bit mode
*/
      if( mode == 6 )
         for( i = 0; i < width; i++ )
            scan_line[i] = scan_line[i] << 2;
      memcpy( ptr, scan_line, width );
      ptr += width;
      }
   }
return 0;
}