// NOTE, if you don't have the netinet/in.h, just comment it out.
// then manually set whether or not to swap the bytes (see commented
// section down in the main, it isn't hard to fix) - dlr
// 
// lvis_release_reader.c
// 
// HOWTO compile:  gcc -Wall lvis_release_reader.c -o lvis_release_reader
// 
// HOWTO execute:
// ./lvis_release_reader LVIS_HB_2003_grid_0.lce
// ./lvis_release_reader LVIS_HOW_2003_grid_0.lce -lon 290.00-291.40 -lat 44.74-44.76
// ./lvis_release_reader LVIS_HOW_2003_Flux_Tower_West_grid_0.lgw -c
// 
// Version 1.0
// Begun: 2004/08/19
// David Lloyd Rabine
// NASA GSFC Code 924.0 Laser Remote Sensing Branch
// david@ltpmail.gsfc.nasa.gov
// 301-614-6771
// http://lvis.gsfc.nasa.gov
//
// EXAMPLE program to convert the LVIS data file formats (LCE, LGW and LGE) 
// to ASCII
// 
// Version 1.01 - 20051028
// added LFID and SHOTNUMBER to the structure
// 
// Version 1.02 - 20060418
// added TIME to the structure (for ocean data, we're going to need time for tide calculations)
// 
//
// Systems Tested on:  Linux Slackware 9.1 && 10.2 xi686
//                     Solaris Ultra 2
//                     Mac OS X
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lvis_release_structures.h"

typedef unsigned char  byte;
typedef unsigned short word;
typedef unsigned long dword;

#ifndef  LVIS_RELEASE_READER_VERSION
# define LVIS_RELEASE_READER_VERSION 1.02
#endif
#ifndef  LVIS_RELEASE_READER_VERSION_DATE
# define LVIS_RELEASE_READER_VERSION_DATE 20060418
#endif
#ifndef  GENLIB_LITTLE_ENDIAN
# define GENLIB_LITTLE_ENDIAN 0x00
#endif
#ifndef  GENLIB_BIG_ENDIAN
# define GENLIB_BIG_ENDIAN 0x01
#endif

// if you want a little more info to start...uncomment this
// #define DEBUG_ON

double host_double(double input_double,int host_endian)
{
   double return_value;
   unsigned char * inptr, * outptr;
   
   inptr = (unsigned char *) &input_double;
   outptr = (unsigned char *) &return_value;
   
   if(host_endian == GENLIB_BIG_ENDIAN)
     {
	outptr[0] = inptr[7];
	outptr[1] = inptr[6];
	outptr[2] = inptr[5];
	outptr[3] = inptr[4];
	outptr[4] = inptr[3];
	outptr[5] = inptr[2];
	outptr[6] = inptr[1];
	outptr[7] = inptr[0];
	return return_value;
     }
   
   if(host_endian == GENLIB_LITTLE_ENDIAN)
     {
	
	return input_double;
     }
   
   return input_double;  // by default just send it back
}

float host_float(float input_float,int host_endian)
{
   float return_value;
   unsigned char * inptr, * outptr;
   
   inptr = (unsigned char *) &input_float;
   outptr = (unsigned char *) &return_value;
   
   if(host_endian == GENLIB_BIG_ENDIAN)
     {
	outptr[0] = inptr[3];
	outptr[1] = inptr[2];
	outptr[2] = inptr[1];
	outptr[3] = inptr[0];
	return return_value;
    }
   
   if(host_endian == GENLIB_LITTLE_ENDIAN)
     {
	return input_float;
     }
   return input_float;  // by default just send it back
}

dword host_dword(dword input_dword,int host_endian)
{
   dword return_value;
   unsigned char * inptr, * outptr;
   
   inptr = (unsigned char *) &input_dword;
   outptr = (unsigned char *) &return_value;
   
   if(host_endian == GENLIB_BIG_ENDIAN)
     {
	outptr[0] = inptr[3];
	outptr[1] = inptr[2];
	outptr[2] = inptr[1];
	outptr[3] = inptr[0];
	return return_value;
    }
   
   if(host_endian == GENLIB_LITTLE_ENDIAN)
     {
	return input_dword;
     }
   return input_dword;  // by default just send it back
}

word host_word(word input_word,int host_endian)
{
   word return_value;
   unsigned char * inptr, * outptr;
   
   inptr = (unsigned char *) &input_word;
   outptr = (unsigned char *) &return_value;
   
   if(host_endian == GENLIB_BIG_ENDIAN)
     {
	outptr[0] = inptr[1];
	outptr[1] = inptr[0];
	return return_value;
     }
   if(host_endian == GENLIB_LITTLE_ENDIAN)
     {	
	return input_word;
     }   
   return input_word;  // by default just send it back
}

int host_endian(void)
{
   int host_endian;
   
   union 
     {
	char c; int i; 
     }
   tester;
   
   tester.i = 0;
   tester.c = 1;
   
   if(tester.i == 1) 
     host_endian = GENLIB_LITTLE_ENDIAN;
   else
     host_endian = GENLIB_BIG_ENDIAN;
   
   return host_endian;
}

void display_usage(char * proggy)
{
   printf("USAGE: %s <input> [options]\n",proggy);
   printf("\n");
   printf("-c                    Delimit data with commas (default = TAB)\n");
   printf("-i                    Generate an index column\n");
   printf("-lat dd.dddd-dd.dddd  Cut by latitude  (min -> max decimal degrees)\n");
   printf("-lon dd.dddd-dd.dddd  Cut by longitude (min -> max decimal degrees)\n");
   printf("-lce                  Force file type to LCE (normally chooses by file extension)\n");
   printf("-lge                  Force file type to LGE\n");
   printf("-lgw                  Force file type to LGW\n");
   printf("-v                    Print program version and exit\n");
   printf("\n");
   printf("\n");
   
}

int main(int argc, char *argv[])
{
   double minlon,maxlon,minlat,maxlat;
   char delim[16],temp[1024],tempa[1204],tempb[1024];
   int i,j,myendian,filetype,indexcol;
   unsigned long colnum;
   
   // make a single shot record of each type
   lvis_lce lce;
   lvis_lge lge;
   lvis_lgw lgw;
   
   FILE *fp;
   // set up variable defaults
   minlat = -400.0; maxlat = 400.0;
   minlon = -400.0; maxlon = 400.0;
   memset(delim,0,sizeof(delim));
   strncpy(delim,"\t",1);
   indexcol=0;

   // figure out what endian this host machine is
   myendian = host_endian();
   
   // less than 2 arguments? we need at least an input file name
   if(argc<2)
     {
	display_usage(argv[0]);
	exit(-1);
     }

   // check for a version flag
   strcpy(temp,argv[1]);
   if(strncmp(temp,"-v",2)==0)
     { 
	minlon = LVIS_RELEASE_READER_VERSION;
	maxlon = LVIS_RELEASE_READER_VERSION_DATE;
	printf("LVIS Release Reader Version %5.2f (%lu)\n",minlon,(dword) maxlon);
	minlon = LVIS_RELEASE_STRUCTURE_VERSION;
	maxlon = LVIS_RELEASE_STRUCTURE_DATE;
	printf("LVIS Release Data Structure %5.2f (%lu)\n",minlon,(dword) maxlon);
	exit(1);
     }

   
   // open up the file for read access
   if((fp = fopen(argv[1],"rb"))==NULL)
     {
	fprintf(stderr,"Error opening the input file: %s\n",argv[1]);
	exit(-1);
     }

   // detect the file type by the extension
   strcpy(temp,argv[1]);
   i = strlen(temp);
   filetype = 0;   // give it a default value of no data
   if(strncmp(&temp[i-3],"lce",3)==0) filetype = LVIS_RELEASE_FILETYPE_LCE;
   if(strncmp(&temp[i-3],"lge",3)==0) filetype = LVIS_RELEASE_FILETYPE_LGE;
   if(strncmp(&temp[i-3],"lgw",3)==0) filetype = LVIS_RELEASE_FILETYPE_LGW;

   // check for command line arguments
   i=2;  // set a pointer at arguement 2 of the command line
   while(i<argc)
     {
	strcpy(temp,argv[i]);
	if(strncmp(temp,"-v",2)==0)
	  { 
	     minlon = LVIS_RELEASE_READER_VERSION;
	     printf("Lvis Release Reader Version %5.2f\n",minlon);
	     exit(1);
	  }
	if(strncmp(temp,"-c",2)==0)
	  { strncpy(delim,",",1);}
	if(strncmp(temp,"-i",2)==0)
	  { indexcol=1; }
	if(strncmp(temp,"-lce",4)==0)
	  { filetype = LVIS_RELEASE_FILETYPE_LCE; }
	if(strncmp(temp,"-lge",4)==0)
	  { filetype = LVIS_RELEASE_FILETYPE_LGE; }
	if(strncmp(temp,"-lgw",4)==0) 
	  { filetype = LVIS_RELEASE_FILETYPE_LGW; }
	if(strncmp(temp,"-lat",4)==0)
	  {
	     i++;
	     if(i<argc) 
	       {	  
		  strcpy(tempa,argv[i]);
		  // find the '-' character in the string
		  j=0; // j will be our pointer
		  while(j++<strlen(tempa) && tempa[j]!='-'); 
		  if(j>=strlen(tempa)) printf("Invalid argument to -lat parameter!\n");
		  else
		    {  // split the string around j into min/max
		       memset(tempb,0,sizeof(tempb));
		       strncpy(tempb,&tempa[0],j);
		       minlat = atof(tempb);
		       memset(tempb,0,sizeof(tempb));
		       strncpy(tempb,&tempa[j+1],strlen(tempa)-j);
		       maxlat = atof(tempb);
		    }
	       }
	  }
	if(strncmp(temp,"-lon",4)==0)
	  {
	     i++;
	     if(i<argc) 
	       {	  
		  strcpy(tempa,argv[i]);
		  // find the '-' character in the string
		  j=0;
		  while(j++<strlen(tempa) && tempa[j]!='-');
		  if(j>=strlen(tempa)) printf("Invalid argument to -lon parameter!\n");
		  else
		    {
		       memset(tempb,0,sizeof(tempb));
		       strncpy(tempb,&tempa[0],j);
		       minlon = atof(tempb);
		       memset(tempb,0,sizeof(tempb));
		       strncpy(tempb,&tempa[j+1],strlen(tempa)-j);
		       maxlon = atof(tempb);
		    }
	       }
	  }
	i++;
     }
   
#ifdef DEBUG_ON  // uncomment the #define up top if you want to see these messages
   printf("input file name = %s (strlen=%d)\n",temp,strlen(temp));
   printf("input file type = %d\n",filetype);
   printf("lce structure size = %d bytes\n",sizeof(lce));
   printf("lge structure size = %d bytes\n",sizeof(lge));
   if(myendian == GENLIB_LITTLE_ENDIAN) printf("system is LITTLE ENDIAN.\n");
   if(myendian == GENLIB_BIG_ENDIAN) printf("system is BIG ENDIAN.\n");
#endif

   colnum = 1;  // set the index column counter to one (1)
   // do this while loop if this data is LGE
   if(filetype==LVIS_RELEASE_FILETYPE_LGE)
     while(fread(&lge,sizeof(lge),1,fp)==1)   // read in the data one block at a time
       {
	  if(myendian==GENLIB_LITTLE_ENDIAN)  // only need to swap if little
	    {
	       // swap each item of this block
	       lge.lfid        = host_dword(lge.lfid,GENLIB_BIG_ENDIAN);
	       lge.shotnumber  = host_dword(lge.shotnumber,GENLIB_BIG_ENDIAN);
	       lge.lvistime    = host_double(lge.lvistime,GENLIB_BIG_ENDIAN);
	       lge.glon  = host_double(lge.glon,GENLIB_BIG_ENDIAN);
	       lge.glat  = host_double(lge.glat,GENLIB_BIG_ENDIAN);
	       lge.zg    = host_float(lge.zg,GENLIB_BIG_ENDIAN);
	       lge.rh25  = host_float(lge.rh25,GENLIB_BIG_ENDIAN);
	       lge.rh50  = host_float(lge.rh50,GENLIB_BIG_ENDIAN);
	       lge.rh75  = host_float(lge.rh75,GENLIB_BIG_ENDIAN);
	       lge.rh100 = host_float(lge.rh100,GENLIB_BIG_ENDIAN);
	    }
	  // print the data in tab delimited columns for this data block
	  if(lge.glon>minlon && lge.glon<maxlon && lge.glat>minlat && lge.glat<maxlat)
	    {  
	       if(indexcol==1) printf("%10li%s",colnum++,delim);
	       printf("%lu%s%lu%s%12.6f%s",lge.lfid,delim,lge.shotnumber,delim,lge.lvistime,delim);
	       printf("%14.10f%s%14.10f%s%9.4f%s",lge.glon,delim,lge.glat,delim,lge.zg,delim);
	       printf("%9.4f%s%9.4f%s%9.4f%s%9.4f\n",lge.rh25,delim,lge.rh50,delim,lge.rh75,delim,lge.rh100);
	       // the format for the %f print is:
	       //     # total number of digits (including decimal) . # digits after the decimal
	    }
	  
       }
   
   // do this while loop if this data is LCE
   if(filetype==LVIS_RELEASE_FILETYPE_LCE)
     while(fread(&lce,sizeof(lce),1,fp)==1) 
       {
	  if(myendian==GENLIB_LITTLE_ENDIAN)  // only need to swap if little
	    {
	       lce.lfid        = host_dword(lce.lfid,GENLIB_BIG_ENDIAN);
	       lce.shotnumber  = host_dword(lce.shotnumber,GENLIB_BIG_ENDIAN);
	       lce.lvistime    = host_double(lce.lvistime,GENLIB_BIG_ENDIAN);
	       lce.tlon = host_double(lce.tlon,GENLIB_BIG_ENDIAN);
	       lce.tlat = host_double(lce.tlat,GENLIB_BIG_ENDIAN);
	       lce.zt = host_float(lce.zt,GENLIB_BIG_ENDIAN);
	    }
	  if(lce.tlon>minlon && lce.tlon<maxlon && lce.tlat>minlat && lce.tlat<maxlat)
	    {  
	       if(indexcol==1) printf("%10li%s",colnum++,delim);
	       printf("%lu%s%lu%s%12.6f%s",lce.lfid,delim,lce.shotnumber,delim,lce.lvistime,delim);
	       printf("%14.10f%s%14.10f%s%9.4f\n",lce.tlon,delim,lce.tlat,delim,lce.zt);
	    }	  
       }

   // do this while loop if this data is LGW   
   if(filetype==LVIS_RELEASE_FILETYPE_LGW)
     while(fread(&lgw,sizeof(lgw),1,fp)==1) 
       {
	  if(myendian==GENLIB_LITTLE_ENDIAN)  // only need to swap if little
	    {
	       lgw.lfid        = host_dword(lgw.lfid,GENLIB_BIG_ENDIAN);
	       lgw.shotnumber  = host_dword(lgw.shotnumber,GENLIB_BIG_ENDIAN);
	       lgw.lvistime    = host_double(lgw.lvistime,GENLIB_BIG_ENDIAN);
	       lgw.lon0 = host_double(lgw.lon0,GENLIB_BIG_ENDIAN);
	       lgw.lat0 = host_double(lgw.lat0,GENLIB_BIG_ENDIAN);
	       lgw.z0 = host_float(lgw.z0,GENLIB_BIG_ENDIAN);
	       lgw.lon431 = host_double(lgw.lon431,GENLIB_BIG_ENDIAN);
	       lgw.lat431 = host_double(lgw.lat431,GENLIB_BIG_ENDIAN);
	       lgw.z431 = host_float(lgw.z431,GENLIB_BIG_ENDIAN);
	       lgw.sigmean = host_float(lgw.sigmean,GENLIB_BIG_ENDIAN);
	    }
	  if(lgw.lon431>minlon && lgw.lon431<maxlon && lgw.lat431>minlat && lgw.lat431<maxlat)
	    {
	       if(indexcol==1) printf("%10li%s",colnum++,delim);
	       printf("%lu%s%lu%s%12.6f%s",lgw.lfid,delim,lgw.shotnumber,delim,lgw.lvistime,delim);
	       printf("%14.10f%s%14.10f%s%9.4f%s",lgw.lon0,delim,lgw.lat0,delim,lgw.z0,delim);
	       printf("%14.10f%s%14.10f%s%9.4f%s",lgw.lon431,delim,lgw.lat431,delim,lgw.z431,delim);
	       printf("%9.4f%s",lgw.sigmean,delim);
	       for(j=0;j<sizeof(lgw.wave)-1;j++) printf("%03d%s",lgw.wave[j],delim);
	       printf("%03d\n",lgw.wave[j]);
	    }
	  
       }
   
   fclose(fp);
   return(1);
}