#include <stdio.h>
#include <string.h>
#include <ctype.h> /* tolower(), toupper(), isalpha() */
#include <stdlib.h>
#define FALSE 0
#define TRUE  1
#define BUF 4096

/* show the help screen*/
void show_help(void)
{
   printf("\nusage: sac2dat OPTION filename.sac\n"\
          "extracting data cycles of SAC-files created by Balzers Quadstar 4.22 Software\n"\
		  "\nOptions:\n"\
          "\t-i\t\tshow information about the sac-file\n"\
          "\t-a [BLKNR]\tall cycles [optional blocknumber]\n"\
		  "\t-s CYNR [BLKNR]\tsingle cycle number [optional blocknumber]\n"\
          "\t-h\t\tthis text\n"
          "\t-v\t\tversion\n\n");
}

/* show what size the variables on your computer should have*/
void showVariableSize(void)
{
	printf("\nExpected variable sizes on computer architecture\n");
	printf("int\texpected %i\tactual %i\n", 4, sizeof(int));
	printf("float\texpected %i\tactual %i\n", 4, sizeof(float));
	printf("short\texpected %i\tactual %i\n", 2, sizeof(short));
	printf("\n");

}


/*show the version screen */
void show_version(void)
{
   printf ("sac2dat\nVersion 0.3 16.10.2019\nMoritz Bubek\nmoritz@bubek.org\n");

   showVariableSize();
}


void printFileHeader(char *author, char *date, char *filename, int cycleX, int blockX)
{
	char vv ='#';

	printf("%c sac2dat", vv);
	printf("  Moritz Bubek, moritz@bubek.org, 2010-2019\n", vv);
	printf("%c \n", vv);
	if (cycleX != 0)
		printf ("%c cycle %i (block %i) of file %s\n",vv, cycleX,blockX, filename);
	else
	    printf ("%c all cycles (block %i) of file %s\n",vv, blockX, filename);

	printf("%c Experimenter: %s\n", vv,author);
	printf("%c date of measurement: %s\n", vv,date);


}


/*get parameter out of the programm parameters, split the params into single parameters*/
int getopt(char *argument,char *option)
{
   if( argument[0]=='-' && argument[1]==option[0] )
      return TRUE;
   return FALSE;
}

/* read the data from file*/
int getData(char *filename, int cycleX, int blockX)
{
		int counter=0,i,j;
		int opt=0;
		//File pointer
		FILE *ptr_myfile;

		//open the data file for reading
		ptr_myfile=fopen(filename,"r");
		if (!ptr_myfile)
		{
			printf("Unable to open file!\n");
			return 1;
		}
/*
All the following readings are done at certain positions and with certain sizes (for example sizeof(int)) which can be found in the manual for SAC files from QUADSTAR
*/
		short int dataIndex;
		fseek(ptr_myfile, 0, SEEK_SET);
		fread(&dataIndex, sizeof(short int),1,ptr_myfile);

		// ID and SoftwareVersion
		char softId[4];
		char softVersionMajor;
		char softVersionMinor;
		fseek(ptr_myfile, 2, SEEK_SET);
		fread(&softId, 4,1,ptr_myfile);
		fread(&softVersionMajor, 1,1,ptr_myfile);
		fread(&softVersionMinor, 1,1,ptr_myfile);

		//date of measuremnt
		char measureDate[6];
		fseek(ptr_myfile, 8, SEEK_SET);
		fread(&measureDate, 6,1,ptr_myfile);

		// author means computer user measuring the data
		char author[20];
		fseek(ptr_myfile, 14, SEEK_SET);
		fread(&author, 20,1,ptr_myfile);

		int nr_cyc;  //Number of cycles
		fseek(ptr_myfile, 100, SEEK_SET);
		fread(&nr_cyc,sizeof(int),1,ptr_myfile);

		short nr_dbc;  //number of datablocks
		fseek(ptr_myfile, 104, SEEK_SET);
		fread(&nr_dbc,sizeof(short),1,ptr_myfile);

		int cycle_length; //length of data cycle
		fseek(ptr_myfile, 106, SEEK_SET);
		fread(&cycle_length,sizeof(int),1,ptr_myfile);

		unsigned long startTime;	//time (GMT) when storage was started (sec)
		fseek(ptr_myfile,194, SEEK_SET);
		fread(&startTime, sizeof(unsigned long),1,ptr_myfile);

		char cyc_type; // cycle type
		int hpos;      // header position
		int dpos;      // data position

        //now we find the header position of every cycle
		//these are written down with pointers in blocks of 9 byte
		//1 byte block type, 4 byte header information, 4 byte measure data
		for (i=0;i<nr_dbc;i++)
   		   {
		   fprintf(stderr, "Block %i\n", i);
		   // we start after the complete file header which has 200 bytes
		   fseek(ptr_myfile, 200+i*9, SEEK_SET);
		   //read the cycle type
		   fread(&cyc_type,sizeof(char),1,ptr_myfile);
		   //read the position of header of the cycle
		   fread(&hpos,sizeof(int),1,ptr_myfile);
		   //read the position of data of the cycle
		   fread(&dpos,sizeof(int),1,ptr_myfile);
		   //if cyc_type is 17, the block contains ScanAnalogData
		   //so we finish the format reading as this porgram only can handle
		   // a single data type (see manual)
		   /*changed 16.10.2019 now we look until the desired blocknumer is reached*/
		   if (cyc_type==17)
		   		if (i == blockX )
				 	{
					//fprintf(stderr,"data block\n");
		       		i=nr_cyc+1;
				    }
		   }

// read from the cycle header
char df;		//data format
fseek(ptr_myfile, hpos, SEEK_SET);
fread(&df,sizeof(char),1,ptr_myfile);

char did;		//data ID
fseek(ptr_myfile, hpos+1, SEEK_SET);
fread(&did,sizeof(char),1,ptr_myfile);

char sid;		// scan ID
fseek(ptr_myfile, hpos+28, SEEK_SET);
fread(&sid,sizeof(char),1,ptr_myfile);

float fmass;		//first mass
fseek(ptr_myfile, hpos+122, SEEK_SET);
//fread(&fmass,sizeof(int),1,ptr_myfile);
fread(&fmass,sizeof(float),1,ptr_myfile);

short sw;		// scan width
fseek(ptr_myfile, hpos+126, SEEK_SET);
fread(&sw,sizeof(short),1,ptr_myfile);

int nr_m;		// number of measuremnts per mass
fseek(ptr_myfile, hpos+128, SEEK_SET);
fread(&nr_m,sizeof(char),1,ptr_myfile);

int anz;	// number of data points
short range; //
float x;  //measurement value

int dx=0*cycle_length;

fseek(ptr_myfile, dpos+dx, SEEK_SET);
fread(&anz,sizeof(int),1,ptr_myfile);


fseek(ptr_myfile, dpos+4+dx, SEEK_SET);
fread(&range,sizeof(short),1,ptr_myfile);


//this is the character we show as escape character for comments in the output
char vv='#';

// the option of the getData function is -1 (file info)
if (cycleX==-1)
   {
   vv='#';
   }

//now we show some information in the header of the output data
		//fprintf(stderr, "DATAIDX %i\n", dataIndex);
		//fprintf(stderr, "  SoftId %s\n", softId);
		//fprintf(stderr, "Software Version %c.%c\n", softVersionMajor,softVersionMinor);

		//fprintf(stderr,"%c cycle %i (%i) is type %d\t%i\t%i\n",vv,counter, 200+9*counter, cyc_type,hpos,dpos);
		//fprintf (stderr,"%c Data Id %d\n",vv,did);
		//fprintf (stderr, "%c Scan Id %d\n",vv,sid);

		char dateStr[22];
		sprintf(dateStr, "%i.%i.%i %i:%i:%i", measureDate[3],measureDate[4],1900+measureDate[5],measureDate[2],measureDate[1],measureDate[0]);
		printFileHeader(author, dateStr, filename, cycleX, blockX);

        printf("%c number of cycles %i\n",vv,nr_cyc);
		printf("%c number of data blocks per cycle %d\n",vv,nr_dbc);
		printf("%c cycle Length %i\n",vv,cycle_length);

		printf("%c Start time (UTC) %i\n",vv,startTime);
        printf ("%c data format %d\n",vv,df);
		printf ("%c first mass %f\n",vv,fmass);
		printf ("%c scan width %i\n",vv,sw);
		printf ("%c number of measured value per mass %i\n",vv,nr_m);
		printf ("%c number of points %i\n",vv,anz);
		printf ("%c Range %i\n",vv,range);
		printf ("%c\n", vv);

/* ----------------
from here we start reading the data
-------------------
*/

   /* allocate some memory for the float-pointers array(=cycles) */
   /* we need an array of floats of number of measurements per cycle times number of cycles*/
   float ** cycle;
   cycle = (float **)malloc(nr_cyc*sizeof(float *));
   if(NULL == cycle)
      {
         fprintf(stderr, "Not enough memory...\n");
         exit(0);
      }

   /* Now we allocate memory for the single values of the i-th cycle */
   for(i=0; i < nr_cyc; i++)
      {
         cycle[i] = (float *)malloc(anz*sizeof(float));
         if(NULL == cycle[i])
            {
               fprintf(stderr,"Not enough memory %d\n",i);
               exit(0);
            }
      }

// we read the time passed since first cycle
unsigned long qqq;
unsigned long * cycleStartTime;
// have some memory..
cycleStartTime = (unsigned long *)malloc(nr_cyc*sizeof(unsigned long));


// now we read nr_cyc of cycles
for (i=0;i<nr_cyc;i++)
  {
  //at first we calculate the offset position of the data of the cycle
  dx=i*cycle_length;

  // read the cycle start time from the header of the cycle
  // !!!!!! this is not the correct position !!!!!!!!
  fseek(ptr_myfile, hpos+i*6, SEEK_SET);
  fread(&qqq,sizeof(unsigned long),1,ptr_myfile);
  cycleStartTime[i]=qqq;

  //the absolute position is the data position dpos plus the offset plus 6 (some header)
  fseek(ptr_myfile, dpos+6+dx, SEEK_SET);
  // read the number of entries
  for (counter=0;counter<anz;counter++)
     {
     fread(&x,sizeof(float),1,ptr_myfile);
     // store it in the array
	 cycle[i][counter]=x;
     }
  }


/*---------------------
  Now we show the data on stdout
  ---------------------
*/
//if parameter of getData is 0, we show all cycles
if (cycleX==0)
  {
  //first show a column descrition

	 printf ("%c mass", vv);
	 for (j=0;j<nr_cyc;j++)
        {
	    printf ("\tcyc%i", j);
	    }
     printf ("\n");

    // now the data
  for (i=0;i<anz;i++)
     {
	 // first column is the mass which is calculated
	 printf ("%e\t", (float)fmass+(float)i*sw/anz);
	 //then following some columns of cycle data
     for (j=0;j<nr_cyc;j++)
        {
	    printf ("%e\t", cycle[j][i]);
	    }
     printf ("\n");
     }
  }
// or we show onlye a special cycle
else if (cycleX>0)
  {
  // if the number of the cycle requested is greater than the number of cycles we show the first one
  if (cycleX > nr_cyc)
     cycleX=1;

    // some header line
	printf ("%c mass\tcyc%i\n", vv, cycleX);

     for (i=0;i<anz;i++)
        {
		// show mass and amplitude
	    printf ("%e\t%e\n", (float)fmass+(float)i*sw/anz, cycle[cycleX][i]);
	    }
  }

// at the end of the getData we close the file
		fclose(ptr_myfile);
		return 0;
}


/* this is the main program */
int main(int argc, char *argv[])
{
   int counter=3;
   char buffer[BUF];
   size_t len=0;
   int dx=0;

   int wBlock = 1;

   // only one argument -> show help
   if(argc == 1 || getopt(argv[1],"h") == TRUE )
      {
         show_help();
         exit(0);
      }
   //argument is -v
   else if(getopt(argv[1],"v") == TRUE)
      {
         // show version screen
		 show_version();
         exit(0);
      }
   //we need at least 3 arguments (program name, parameter, data file)
   else if(argc < 3)
      {
         show_help();
         exit(0);
      }

   // length of the filename parameter
   len=strlen(argv[2])+1;
   if(len > BUF)
     {
        printf("Filename too long\n");
        exit(0);
     }
   //copy data filename into buffer
   strcpy(buffer,argv[2]);
   /*from argv[2] to argv[n] all elements into buffer*/

   while(argv[counter]!=NULL)
      {
         len += strlen(argv[counter])+2;
         if(len > BUF)
            {
               printf("Buffer full\n");
               break;
            }
         strcat(buffer, " ");
         strcat(buffer, argv[counter++]);
      }

   //if argument -i , show the file info
   //which is done by parameter -1 in getData
   if(getopt(argv[1],"i") == TRUE)
      getData(argv[2],-1,1);
   // if argument -a getData for all cycles
   else if(getopt(argv[1],"a") == TRUE)
   	  {
	  int filenameArgument=2;

	  // check for addional block argument (second number)
  	  if (argc == 4)
	    {
        wBlock = atoi(argv[2]);
		filenameArgument=3;
	    }
      getData(argv[filenameArgument],0,wBlock);
      }
   //if argument -s we select a special cycle
   else if(getopt(argv[1],"s") == TRUE)
      {
	  //now we need 4 parameters (additional number of the cycle)
	  if (argc < 4)
	     {
		 show_help();
		 exit(0);
		 }
	  else
	    {
		// so we have enough parameters
		// get the number of the cylce wanted from argv[2]
		int wCycle= atoi(argv[2]);

		int filenameArgument=3;
		//check also for additional block argument
		if (argc == 5)
			{
				filenameArgument=4;
				wBlock = atoi(argv[3]);
			}

		// now get the data from this cycle
		getData(argv[filenameArgument], wCycle, wBlock);
		}
	  }
   else
      // no matches at all? so show help
      show_help();
   return 0;
}
