/****************************************************************************/
/* Copyright 2005 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : MOOS Power System Data Aquistion Software                     */
/* Author   : Mike Risi, Kent Headley                                       */
/* Created  : 09/30/2005                                                    */
/****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "log_hdr.h"


#define VERSION "1.0"

/* Function definitions */
void byteSwap32(char* bytes);
void byteSwap16(char* bytes);
int readLogFileHeader(LogFileHeader* hdr, FILE* file);
void processFile(char *inFileName );
void printUsage(int exitCode);
void showSettings();

/* Global Variables */

/* Header output modes */
int HEADERS_ALL=1;
int HEADERS_FIRST=2;
int HEADERS_NONE=3;

/* Option defaults */
int HEADERS_DFLT=1;
int MODULUS_DFLT=1;
int doTime=0;
int doChannel=0;
int doSamples=0;
int doPeriod=0;
int doGain=0;
int doOffset=0;
int doRaw=0;
int doCal=1;
int modulus=0;
int headers=0;
int verbose=1;
int debug=0;

/* Entry point */
int main(int argc, char *argv[]) 
{
    int i;
    short raw_data;
    float cal_data;
    FILE* in_file = NULL;
    FILE* out_file = NULL;
    LogFileHeader log_header;
    char out_file_name[80];
    char time_str[80];
    char in_file_name[80];
    int c;
    int dirty=0;
    modulus=MODULUS_DFLT;
    headers=HEADERS_DFLT;

    /* if no args, print use message and exit */
    if(argc==1)
      printUsage(0);

    /* process command line arguments */
    while ( (c = getopt(argc,argv,"cCdgNopqrsStn:")) != -1)
      switch(c){

      case 'c':
	doChannel=1;
	break;
      case 'C':
	doCal=0;
	break;
      case 'd':
	debug=1;
	break;
      case 'g':
	doGain=1;
	break;
      case 'n':
	sscanf(optarg,"%d",&modulus);
	break;
      case 'N':
	doSamples=1;
	break;
      case 'o':
	doOffset=1;
	break;
      case 'p':
	doPeriod=1;
	break;
      case 'r':
	doRaw=1;
	break;
      case 's':
	headers=HEADERS_FIRST;
	break;
      case 'S':
	headers=HEADERS_NONE;
	break;
      case 't':
	doTime=1;
	break;
      case 'q':
	verbose=0;
	break;
      case '?':
	if (isprint (optopt))
	  fprintf (stderr, "Unknown option `-%c'.\n", optopt);
	else
	  fprintf (stderr,
		   "Unknown option character `\\x%x'.\n",
		   optopt);
	return 1;
      default:
	abort ();
      }

    /* display version and build information */
    if(verbose){
      printf("\nadcDecode version " VERSION ", built on " \
	     __DATE__  " at " __TIME__ "\n");

      /* show configuration, per command line options  */
      if(debug!=0)
	showSettings();
    }

    /* process the specified files */
    for (i = optind; i < argc; i++){
      if(debug!=0)
	printf ("processing file: %s\n", argv[i]);
      processFile(argv[i]);
    }

    exit(0);
}

/* read raw files and format data output */
void processFile(char *inFileName){
  int i;
  short raw_data;
  float cal_data;
  FILE* inFile = NULL;
  FILE* outFile = NULL;
  LogFileHeader log_header;
  char outFileName[256];
  char time_str[80];
  int isFirst=1; 
  double dTime;
  double dTimeInc;
  long lTime;
  int dirty=0;
  int flushCount=0;

  /* open the log file for reading */
  inFile = fopen(inFileName, "rb");

  if ( inFile == NULL ){
    fprintf(stderr, "failed to open input file %s\n", inFileName);
    fclose(inFile);
    return;
  }
  
  /* open an output file for the parsed data */
  
  /* create output file name */
  strcpy(outFileName, inFileName);
  strcat(outFileName, ".txt");
  
  outFile = fopen(outFileName, "w");
  
  if ( outFile == NULL ){
    fprintf(stderr, "failed to open output file %s\n", outFileName);
    fclose(inFile);
    fclose(outFile);  
    return;
  }
    
  /* read out ints until they are all gone */
  while ( readLogFileHeader(&log_header, inFile) ){

    /* fix ctime format */
    strcpy(time_str, ctime(&log_header.start_time));
    if (strlen(time_str) > 1)
      time_str[strlen(time_str) - 1] = '\0';
    
    if(verbose){
      printf("ADC CHANNEL         : %ld\n", log_header.adc_chan);
      printf("SAMPLE PERIOD       : %lu ms\n", log_header.sample_period);
      printf("FIRST SAMPLE LOGGED : %s\n", time_str);
      printf("TOTAL SAMPLES       : %ld\n", log_header.total_samples);
      printf("CALIBRATION OFFSET  : %f\n", log_header.cal_offset);
      printf("CALIBRATION GAIN    : %f\n", log_header.cal_gain);
    }

    dTime=(double)log_header.start_time;
    dTimeInc=(double)((double)log_header.sample_period/(double)1000.0);

    /* write log file header to the output file */
    if( headers==HEADERS_ALL  || (headers==HEADERS_FIRST && isFirst) ){

      fprintf(outFile, "%%ADC CHANNEL         : %d\r\n", 
	      (int)log_header.adc_chan);
      fprintf(outFile, "%%SAMPLE PERIOD       : %lu ms\r\n", 
	      log_header.sample_period);
      fprintf(outFile, "%%FIRST SAMPLE LOGGED : %s\r\n", 
	      time_str); 
      fprintf(outFile, "%%TOTAL SAMPLES       : %ld\r\n", 
	      log_header.total_samples);
      fprintf(outFile, "%%CALIBRATION OFFSET  : %f\r\n", 
	      log_header.cal_offset);
      fprintf(outFile, "%%CALIBRATION GAIN    : %f\r\n", 
	      log_header.cal_gain);
      isFirst=0;
    }

    for (i = 0; i < log_header.total_samples; i++){

      /* grab a sample */
      fread(&raw_data, sizeof(short), 1, inFile);

      /* decimate data, per specified modulus */
      if( i%modulus==0 ){

	/* the raw_data is in big endian order, swap it */
	byteSwap16((char*)&raw_data);

	/* apply the cal factors */
	cal_data = (raw_data * log_header.cal_gain) + log_header.cal_offset;

	/* print specified output components to output file */
	dirty=0;
	if(doTime){
	  fprintf(outFile,"%.3lf",dTime);
	  dirty=1;
	}
	if(doChannel){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%d",(int)log_header.adc_chan);
	  dirty=1;
	}
	if(doPeriod){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%lu",log_header.sample_period);
	  dirty=1;
	}
	if(doSamples){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%ld",log_header.total_samples);
	  dirty=1;
	}
	if(doGain){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%f",log_header.cal_gain);
	  dirty=1;
	}
	if(doOffset){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%f",log_header.cal_offset);
	  dirty=1;
	}
	if(doRaw){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%hd",raw_data);
	  dirty=1;
	}
	if(doCal){
	  if(dirty)
	    fprintf(outFile,",");
	  fprintf(outFile,"%f",cal_data);
	}
	fprintf(outFile,"\r\n");
	if((flushCount++)%1024==0)
	  fflush(outFile);
      }

      dTime+=dTimeInc;
    }
  }
  
  /* close the log file */
  fclose(inFile);
  fclose(outFile);
  
  return;

}

/* print use message and exit */
void printUsage(int exitCode){
  fprintf(stderr, "\nusage: adcDecode [-cdgnNopqsSt] [logfile]\n\n");

  fprintf(stderr, " c     : include channel in output record            [%s]\n",doChannel?"TRUE":"FALSE");
  fprintf(stderr, " C     : suppress calibrated value in output record  [%s]\n",doCal?"FALSE":"TRUE");
  fprintf(stderr, " d     : enable debug output                         [%s]\n",debug?"TRUE":"FALSE");
  fprintf(stderr, " g     : include gain in output record               [%s]\n",doGain?"TRUE":"FALSE");
  fprintf(stderr, " n <N> : print every Nth record                      [%d]\n",MODULUS_DFLT);
  fprintf(stderr, " N     : include total samples in output record      [%s]\n",doSamples?"TRUE":"FALSE");
  fprintf(stderr, " o     : include offset in output record             [%s]\n",doOffset?"TRUE":"FALSE");
  fprintf(stderr, " p     : include period in output record             [%s]\n",doPeriod?"TRUE":"FALSE"); 
  fprintf(stderr, " q     : disable verbose output                      [%s]\n",verbose?"FALSE":"TRUE");
  fprintf(stderr, " r     : include raw counts in output record         [%s]\n",doRaw?"TRUE":"FALSE");
  fprintf(stderr, " s     : suppress all headers after the first        [%s]\n",
	  HEADERS_DFLT==HEADERS_ALL?"ALL":HEADERS_DFLT==HEADERS_NONE?"NONE":"FIRST");
  fprintf(stderr, " S     : suppress all headers                        [%s]\n",
	  HEADERS_DFLT==HEADERS_ALL?"ALL":HEADERS_DFLT==HEADERS_NONE?"NONE":"FIRST");
  fprintf(stderr, " t     : include time in output record               [%s]\n",doTime?"TRUE":"FALSE");

  fprintf(stderr, "\n\n");
  fprintf(stderr, "Examples:\n\n");
  fprintf(stderr,"process file 1.002, process every 1024th sample, suppress\nheaders, output format: time,channel,calibratedOutput\n");
  fprintf(stderr, "\tadcDecode -Sqtcn 1024 1.002\n\n");
  fprintf(stderr,"process file 1.002, process all samples, include first header\n only, output format: time,channel,gain,offset,rawOutput\n");
  fprintf(stderr, "\tadcDecode -sqtcgorC 1.002\n");
  fprintf(stderr, "\n\n");

  exit(exitCode);
}

/* display current option settings */
void showSettings(){
  int dirty=0;

  fprintf(stdout,"headers=%s debug=%s modulus=%d\n",
	  headers==HEADERS_ALL?"ALL":headers==HEADERS_NONE?"NONE":"FIRST",
	  debug?"TRUE":"FALSE",
	  modulus);

  fprintf(stdout,"output record format: ");
  dirty=0;
  if(doTime){
    fprintf(stdout,"time");
    dirty=1;
  }
  if(doChannel){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"adcChan");
    dirty=1;
  }
  if(doPeriod){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"samplePeriod");
    dirty=1;
  }
  if(doSamples){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"totalSamples");
    dirty=1;
  }
  if(doGain){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"calGain");
    dirty=1;
  }
  if(doOffset){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"calOffset");
    dirty=1;
  }
  if(doRaw){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"rawCounts");
    dirty=1;
  }
  if(doCal){
    if(dirty)
      fprintf(stdout,",");
    fprintf(stdout,"calData");
  }
  fprintf(stdout,"\r\n\r\n");
}

/* read data log header entry */
int readLogFileHeader(LogFileHeader* hdr, FILE* file)
{
    /* read the header and display */
    int cnt = fread(hdr, sizeof(LogFileHeader), 1, file);

    if ( cnt != 1 ) 
        return 0;

    /* this is from a big endian processor */
    byteSwap32((char*)&hdr->adc_chan);
    byteSwap32((char*)&hdr->sample_period);
    byteSwap32((char*)&hdr->start_time);
    byteSwap32((char*)&hdr->total_samples);    
    byteSwap32((char*)&hdr->cal_gain);
    byteSwap32((char*)&hdr->cal_offset);

    return cnt;
}

/* perform 32 bit byte swapping */
void byteSwap32(char* bytes)
{
    char swap_buff[4];
    
    swap_buff[0] = bytes[0];
    swap_buff[1] = bytes[1];
    swap_buff[2] = bytes[2];
    swap_buff[3] = bytes[3];
    
    bytes[0] = swap_buff[3];
    bytes[1] = swap_buff[2];
    bytes[2] = swap_buff[1];
    bytes[3] = swap_buff[0];
}


/* perform 16 bit byte swapping */
void byteSwap16(char* bytes)
{
    char swap_buff[2];
    
    swap_buff[0] = bytes[0];
    swap_buff[1] = bytes[1];
    
    bytes[0] = swap_buff[1];
    bytes[1] = swap_buff[0];
}

