/*
 * Usage:  ./newrex_c data/20070105_003031/rex_003031500?_*.fit
 *
 * Extract and output table of data from New Horizons REX data sets,
 * using PDS label to locate data within FITS files.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Swap bytes in-place for n-byte value */
void doswap(void* pv, int n) {
char c1;
static int16_t i2= 256;
char* p = (char*) &i2;
  if (*p) return;
  for ( --n, p=pv; n>0; p++, n-=2 ) {
    c1 = *p;
    *p = p[n];
    p[n] = c1;
  }
  return;
}

/* Structure to aid in parsing PDS label statement ^POINTER = ( "FILE.FIT", 1) */
typedef struct KVstr {
  int count;
  char line[100];
  char kwd[80];
  char tokens[2][80];
  long nCount;
  long n;
} KV;

/* Parse PDS label statement ^POINTER = ( "FILE.FIT", 1) */
KV *readKVPointer(FILE* f) {
static KV kv;                                            /* KV structure for return pointer */
char tmp[100];
char* pLine;
int n;

  kv.nCount = kv.count = -1;                             /* kv.count = -1 => failure e.g. EOF */
  kv.tokens[0][0] = kv.tokens[1][0] = '\0';

  if ( !fgets( kv.line, 100, f) ) return &kv;            /* Read one line from PDS label file stream; return on failure (e.g. EOF) */
  kv.count = 0;                                          /* No EOF, set kv.count to zero */
  if ( (pLine=strchr(kv.line,'\n')) ) *pLine = '\0';     /* Null-terminate at newline, if present */
  if ( (pLine=strchr(kv.line,'\r')) ) *pLine = '\0';     /* Null-terminate at carriage return, if present */
  for ( pLine=kv.line; *pLine && *pLine==' '; ++pLine ); /* Set pointer to first non-space character */

  /* Read keyword, equals sign + open paren + whitespace, filename,
   * comma and whitespace, integer pointer to data record
   */
                   /* %kwd  %=("    %X.F" %,   %N        */
  n = sscanf( pLine, "%[^ =]%[ =(\"]%[^ ,]%[ ,]%[^)]%[)]", kv.kwd, tmp, kv.tokens[0], tmp, kv.tokens[1], tmp );

  if (n!=6) {                 /* Set kv.count to sscanf return value if six items were not read */
    if (n>-1) kv.count = n;   /* and if sscanf did not return -1 (failure?) */
    return &kv;
  }
  kv.count = 2;                                     /* Six parameters strings were read, set count to 2 for pointer filename and record number */
  kv.nCount = sscanf( kv.tokens[1], "%ld", &kv.n);  /* Try to read record number string */
  return &kv;
}


/* Read PDS labels, output table of 
 * values from corresponding FITS file
 */
int main(int ac, char** av) {
int iAc;

float iqCal[1250][2];   /* IQ, Calibrated */
float rtCal[10][2];     /* Radiometry and Time Tags (RT), Calibrated */
int16_t iqRaw[1250][2]; /* IQ, Raw */
struct RTRAW {
  int64_t rad;
  int32_t ttg;
} rtRaw[10];            /* Radiometry and Time Tags (RT), Raw */

KV *pKV;                /* Pointer to structure for parsing PDS label pointers */
FILE* f;                /* File stream pointer */

  for (iAc=1; iAc<ac; ++iAc) {     /* Loop over PDS label filepath arguments */
  long iqRec0 = 0;                 /* Reset FITS IQ pointer */
  long rtRec0 = 0;                 /* Reset FITS RT pointer */
  int isRaw = 0;                   /* Flag; is non-zero (true) if FITS is Raw data */
  int i,j;                         /* Misc counters */
    f = fopen(av[iAc],"r");             /* Open PDS label */
    while ( iqRec0<1 || rtRec0<1 ) {    /* Loop until IQ and RT pointers found */
      pKV = readKVPointer(f);           /* Parse one line */
      if  ( pKV->count<0 ) {                       /* Exit if EOF */
        perror("EOF; exiting"); return 1;
      };
      if  ( pKV->count<1 ) continue;               /* Ignore blank lines */
      if  ( pKV->count==2  && pKV->nCount==1 ) {   /* Check for pointer */
        if ( ! strcmp( pKV->kwd, "^EXTENSION_IQVALS_TABLE") ) {   /* IQ FITS BINTABLE pointer */
          iqRec0 = pKV->n;                                        /* Save record pointer */
          isRaw = strstr(pKV->tokens[0],"_ENG_") ? 1 : 0;         /* _ENG_ in filename pointer means this is raw data */
          continue;                                               /* Continue to next iteration through loop */
        }
        if ( ! strcmp( pKV->kwd, "^EXTENSION_RAD_TIME_TAGS_TABLE") ) {  /* RT FITS BINTABLE pointer */
          rtRec0 = pKV->n;                                              /* Save record pointer */
          continue;                                                     /* Continue to next iteration */
        }
      }
      if ( ! strcmp( pKV->kwd, "END" ) ) {               /* Error if END found */
        perror("PDS END statement; exiting"); return 1;
      }
    }              /* End of PDS label parsing while loop */
    fclose(f);                                            /* Close PDS label */
    strcpy(av[iAc]+strlen(av[iAc])-3,"fit");              /* Build FITS filepath - replace lbl with fit */
    f = fopen(av[iAc],"r");                               /* Open FITS file stream */

    fseek( f, 2880L * (iqRec0 - 1), SEEK_SET);                          /* Seek to IQ BINTABLE */
    if ( isRaw) {
      fread( iqRaw[0], 1, sizeof iqRaw, f);                             /* - Read Raw data */
      for (i=0;i<1250;++i) for (j=0;j<2;++j) doswap(iqRaw[i]+j,2);      /* - Byte-swap Raw data */
    } else {
      fread( iqCal[0], 1, sizeof iqCal, f);                             /* - Read Calibrated data */
      for (i=0;i<1250;++i) for (j=0;j<2;++j) doswap(iqCal[i]+j,4);      /* - Byte-swap Raw data */
    }

    // RT
    fseek( f, 2880L * (rtRec0 - 1), SEEK_SET);                          /* Seek to IQ BINTABLE */
    if ( isRaw) {
      for (i=0; i<10; ++i) {
        fread( rtRaw+i, 1, 12, f);       /* - Read Calibrated data one row at a time */
        doswap(&rtRaw[i].rad,8);         /* - Byte-swap Calibrated Radiometry */
        doswap(&rtRaw[i].ttg,4);         /* - Byte-swap Calibrated Time Tags */
      }
    } else {
      fread( rtCal[0], 1, sizeof rtCal, f);                             /* - Read Calibrated data */
      for (i=0;i<10;++i) for (j=0;j<2;++j) doswap(rtCal[i]+j,4);        /* - Byte-swap Raw data */
    }
    fclose(f);                           /* Close FITS file stream */

    /* Output
     * - 10 rows of row number + IQ + RT data
     * - 1250 rows of row number + IQ data
     */
    if ( isRaw) {
      for (i=0; i<10; ++i) fprintf(stdout, "%5d%16d%16d%16ld%16d\n", i+1, iqRaw[i][0], iqRaw[i][1], rtRaw[i].rad, rtRaw[i].ttg);
      for (i=10; i<1250; ++i) fprintf(stdout, "%5d%16d%16d\n", i+1, iqRaw[i][0], iqRaw[i][1]);
    } else {
      for (i=0; i<10; ++i) fprintf(stdout, "%5d%16.4f%16.4f%16.4f%16.4f\n", i+1, iqCal[i][0], iqCal[i][1], rtCal[i][0], rtCal[i][1]);
      for (i=10; i<1250; ++i) fprintf(stdout, "%5d%16.4f%16.4f\n", i+1, iqCal[i][0], iqCal[i][1]);
    }
  }
  return 0;
}