/*********************************************************************
*
*  Task DBCON
*
* Task to convert from 1 data format to another.
*
* CODE  0 -> CONVERT BASIC BINARY INTEGER TO STANDARD FORMAT
*             0 -> REAL TIME SERIES
*            10 -> TIME LABELED FILE
*            20 -> COMPLEX TIME SERIES
*            30 -> COMPLEX TIME LABELED FILE
*       1 -> CONVERT ASCII TIME SERIES TO STANDARD FORMAT
*             1 -> REAL TIME SERIES
*            11 -> TIME LABELED FILE
*            21 -> COMPLEX TIME SERIES
*            31 -> COMPLEX TIME LABELED FILE
*       2 -> CONVERT 2 BYTE BINARY INTEGER TO STANDARD FORMAT
*             2 -> REAL TIME SERIES
*            12 -> TIME LABELED FILE
*            22 -> COMPLEX TIME SERIES
*            32 -> COMPLEX TIME LABELED FILE
*       3 -> CONVERT STANDARD FORMAT TO BASIC BINARY FORMAT
*       4 -> CONVERT STANDARD FORMAT TO ASCII TIME SERIES
*             4 -> CURRENT FILE TYPE
*            14 -> TIME LABELED FILE OF CURRENT TYPE
*       5 -> CONVERT STANDARD FORMAT TO 2 BYTE BINARY INTEGER
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <malloc.h>
#include <io.h>
#include <time.h>
#include <dos.h>
#include <process.h>
#include <ctype.h>
#include <signal.h>

#include "TISAN.H"

short BTOS(void);  /* BASIC to standard   */
short ATOS(void);  /* ASCII to standard   */
short ITOS(void);  /* Integer to standard */
short STOB(void);  /* Standard to BASIC   */
short STOA(short);   /* Standard to ASCII   */
short STOI(void);  /* Standard to Integer */

short READASCII(double *);  /* Read in an ASCII value         */
short READINT(double *);    /* Read in a binary integer value */
short FPEERROR(void);

char BUFFER[2048];            /* General I/O Buffer */
char CONBUF[sizeof(struct TXData)]; /* Largest Data Type */
char C, TMPFILE[_MAX_PATH];
char INFILE[_MAX_PATH], OUTFILE[_MAX_PATH], *CPNTR;
double TOTAL;
FILE *INSTR, *OUTSTR;
struct FILEHDR FileHeader;
short I;
struct RData  *RDataPntr;
struct TRData *TRDataPntr;
struct XData  *XDataPntr;
struct TXData *TXDataPntr;

main(argc,argv)
short argc;
char *argv[];
   {
   short ERRFLAG = 0;
   short WhatType, WhichConversion;
   struct CATSTRUCT *CatList;
   int I;
   char *pChar;
   char Drive[_MAX_DRIVE], Dir[_MAX_DIR], Fname[_MAX_FNAME], Ext[_MAX_EXT];

   signal(SIGINT,BREAKREQ);  /* Setup ^C Interrupt */

   signal(SIGFPE,FPEERROR);  /* Setup Floating Point Error Trap */

   if (Ztskinit("DBCON",argv[0])) Zexit(1);  /* Initialize task */

   ZBuildFileName(M_inname,INFILE);    /* Build Primary File Name */

   if (!(CatList = ZCatFiles(INFILE))) Zexit(1); /* Find Matching Files */
/*
** For Each Matching File, Perform the Conversion.
** For this simple task it is not necessary to reload the
** adverbs for each iteration since we don't change anyting critical.
*/
   WhatType =  CODE / 10;
   if ((WhatType> 3) || (WhatType < 0)) WhatType = 0;
   WhichConversion = CODE % 10;

   for (I = 0, pChar = CatList->pList;
        (I < CatList->N) && !ERRFLAG;
        ++I, pChar = strchr(pChar,'\0') + 1)
      {
      TOTAL = 0.;
      _splitpath(pChar,Drive,Dir,Fname,Ext);
      strcpy(INNAME,Fname);
      strcpy(INCLASS,Ext);
      ZBuildFileName(M_inname,INFILE);    /* Build file names */
      ZBuildFileName(M_outname,OUTFILE);
      ZBuildFileName(M_tmpname,TMPFILE);
/*
** Open both the input and output files.  If an error occurs then
** the task dies.
*/
      Zencode(2,"DBCON   : Opening Input File '%s'\n",INFILE);
      if ((INSTR = Zopen(INFILE,O_readb)) == NULL) Zexit(1);
      Zencode(2,"DBCON   : Opening Scratch File '%s'\n",TMPFILE);
      if ((OUTSTR = Zopen(TMPFILE,O_writeb)) == NULL) Zexit(1);
     
      FileHeader.b = 0;  /* Initialize time intercept value */
      FileHeader.m = 1;  /* Initialize time slope value     */
      switch (WhatType)
         {
         case 0:
            FileHeader.type = R_Data;
            break;
         case 1:
            FileHeader.type = TR_Data;
            break;
         case 2:
            FileHeader.type = X_Data;
            break;
         case 3:
            FileHeader.type = TX_Data;
            break;
         }

      RDataPntr =  (struct RData *)CONBUF;   /* Data Structure Pointers */
      TRDataPntr = (struct TRData *)CONBUF;
      XDataPntr =  (struct XData *)CONBUF;
      TXDataPntr = (struct TXData *)CONBUF;

      switch (WhichConversion)
         {
         case 0:    /* Convert BASIC binary file to standard format */
            ERRFLAG = BTOS();
            break;
         case 1: /* ASCII to Standard */
            ERRFLAG = ATOS();
            break;
         case 2: /* 2 Byte Binary Integer to Standard */
            ERRFLAG = ITOS();
            break;
         case 3: /* Standard to BASIC Binary */
            ERRFLAG = STOB();
            break;
         case 4: /* Standard to ASCII */
            ERRFLAG = STOA(WhatType);
            break;
         case 5: /* Standard to 2 Byte Binary Integer */
            ERRFLAG = STOI();
            break;
         default:
            Zencode(10,"DBCON   : Unknown Action Code '%d'\n",CODE);
            ERRFLAG = 1;
         }

      if (!ERRFLAG) Zencode(2,"DBCON   : %G Data Points Processed\n",TOTAL);
      Zclose(INSTR);
      Zclose(OUTSTR);
      ERRFLAG = Znameout(OUTFILE,TMPFILE,ERRFLAG);
      }
   Zexit(ERRFLAG);
   }

/*********************************************************************
**
** Basic Binary Integer to Standard Format
*/
short BTOS()
   {
   short FILELEN, DFLAG=1;
   double X, Y, T;

   C = fgetc(INSTR);     /* Check that first byte of file if 0xFD */
   if (C != (char)0xFD)  /* If not then this is not a BASIC file  */
      {
      Zencode(10,"DBCON   : '%s' is not a BASIC binary file\n",INFILE);
      return(1);
      }
   for (I=0;I<4;++I) C = fgetc(INSTR); /* Read past segment and offset */
   fread(&FILELEN,2,1,INSTR);  /* Read in number of bytes in file */
   FILELEN /= 2;
   if (ferror(INSTR))  /* Check for errors on reading input file */
      {
      Zerror();
      return(1);
      }
   else Zencode(2,"DBCON   : File contains %d Integers\n",FILELEN);
   if (Zputhead(OUTSTR,&FileHeader)) return(1);  /* Write out header */
   while (DFLAG)            /* Use DFLAG to signal end of input file */
      {
      switch (FileHeader.type) /* Select data interpretation */
         {
         case R_Data:    /* Real time series */
            if ((DFLAG = READINT(&Y)) > 0)
               {
               RDataPntr->y = Y;
               RDataPntr->f = 0;
               }
            break;
         case TR_Data:   /* Real time labeled */
            if (((DFLAG = READINT(&T)) > 0) &&
                ((DFLAG = READINT(&Y)) > 0))
               {
               TRDataPntr->t = T;
               TRDataPntr->y = Y;
               }
            break;
         case X_Data:  /* Complex time series */
            if (((DFLAG = READINT(&X)) > 0) &&
                ((DFLAG = READINT(&Y)) > 0))
               {
               XDataPntr->z.x = X;
               XDataPntr->z.y = Y;
               XDataPntr->f = 0;
               }
            break;
         case TX_Data:  /* Complex time labeled */
            if (((DFLAG = READINT(&T)) > 0) &&
                ((DFLAG = READINT(&X)) > 0) &&
                ((DFLAG = READINT(&Y)) > 0))
               {
               TXDataPntr->t = T;
               TXDataPntr->z.x = X;
               TXDataPntr->z.y = Y;
               }
            break;
         }
      if (ferror(INSTR))
         {
         Zerror();
         return(1);
         }
      if (DFLAG > 0)
         {
         if (Zwrite(OUTSTR,CONBUF,FileHeader.type)) return(1);
         ++TOTAL;  /* Count number of writes */
         }
      }
   return(0);
   }

/*********************************************************************
**
** ASCII to Standard Format
*/
short ATOS()
   {
   double Y, X, T;
   short DFLAG=1;

   if (Zputhead(OUTSTR,&FileHeader)) return(1);
   while (DFLAG)  /* Use DFLAG to signal end of input file */
      {
      switch (FileHeader.type) /* Select data interpretation */
         {
         case R_Data:  /* Real time series */
            if ((DFLAG = READASCII(&Y)) > 0)
               {
               RDataPntr->y = Y;
               RDataPntr->f = 0;
               }
            break;
         case TR_Data:  /* Real time labeled */
            if (((DFLAG = READASCII(&T)) > 0) &&
                ((DFLAG = READASCII(&Y)) > 0))
               {
               TRDataPntr->t = T;
               TRDataPntr->y = Y;
               }
            break;
         case X_Data:  /* Complex time series */
            if (((DFLAG = READASCII(&X)) > 0) &&
                ((DFLAG = READASCII(&Y)) > 0))
               {
               XDataPntr->z.x = X;
               XDataPntr->z.y = Y;
               XDataPntr->f = 0;
               }
            break;
         case TX_Data:  /* Complex time labeled */
            if (((DFLAG = READASCII(&T)) > 0) &&
                ((DFLAG = READASCII(&X)) > 0) &&
                ((DFLAG = READASCII(&Y)) > 0))
               {
               TXDataPntr->t = T;
               TXDataPntr->z.x = X;
               TXDataPntr->z.y = Y;
               }
            break;
         }
      if (DFLAG < 0) return(1);
      if (DFLAG)
         {
         if (Zwrite(OUTSTR,CONBUF,FileHeader.type)) return(1);
         ++TOTAL;
         }
      }
   return(0);
   }

/*********************************************************************
**
** 2 Byte Binary Integer to Standard Format
*/
short ITOS()
   {
   short DFLAG=1;
   double X, Y, T;

   if (Zputhead(OUTSTR,&FileHeader)) return(1);
   while (DFLAG) /* Use DFLAG to signal end of input file */
      {
      switch (FileHeader.type) /* Select data interpretation */
         {
         case R_Data:   /* Real time series */
            if (DFLAG = READINT(&Y))
               {
               RDataPntr->y = Y;
               RDataPntr->f = 0;
               }
            break;
         case TR_Data:   /* Real time labeled */
            if ((DFLAG = READINT(&T)) &&
                (DFLAG = READINT(&Y)))
               {
               TRDataPntr->t = T;
               TRDataPntr->y = Y;
               }
            break;
         case X_Data:  /* Complex time series */
            if ((DFLAG = READINT(&X)) &&
                (DFLAG = READINT(&Y)))
               {
               XDataPntr->z.x = X;
               XDataPntr->z.y = Y;
               XDataPntr->f = 0;
               }
            break;
         case TX_Data:   /* Complex time labeled */
            if ((DFLAG = READINT(&T)) &&
                (DFLAG = READINT(&X)) &&
                (DFLAG = READINT(&Y)))
               {
               TXDataPntr->t = T;
               TXDataPntr->z.x = X;
               TXDataPntr->z.y = Y;
               }
            break;
         }
      if (ferror(INSTR))
         {
         Zerror();
         return(1);
         }
      if (DFLAG > 0)
         {
         if (Zwrite(OUTSTR,CONBUF,FileHeader.type)) return(1);
         ++TOTAL;
         }
      }
   return(0);
   }

/*********************************************************************
**
** Standard Format to BASIC Binary
*/
short STOB()
   {
   short FILELEN, X, Y, T, F=0;
   struct FILEHDR FH;

   if (!Zgethead(INSTR,&FH)) return(1);           /* Read Header      */
   while (Zread(INSTR,CONBUF,FH.type)) ++TOTAL; /* Find File Length */
   if (!Zgethead(INSTR,(struct FILEHDR *)NULL)) /* Rewind file  */
      return(1);
   fputc(0xFD,OUTSTR); /* Always FD for a BSAVE file */
   fputc(0xDF,OUTSTR); /* Write out rest of BASIC header info */
   fputc(0x14,OUTSTR);
   fputc(0xA3,OUTSTR);
   fputc(0x72,OUTSTR);
   FILELEN = (short)(TOTAL*2);
   fwrite(&FILELEN,2,1,OUTSTR); /* size of file */
   while (Zread(INSTR,CONBUF,FH.type))
      {
      switch (FH.type)
         {
         case R_Data:
            Y = (short)RDataPntr->y;
            fwrite(&Y,2,1,OUTSTR);
            fwrite(&F,2,1,OUTSTR);
            break;
         case TR_Data:
            T = (short)TRDataPntr->t;
            Y = (short)TRDataPntr->y;
            fwrite(&T,2,1,OUTSTR);
            fwrite(&Y,2,1,OUTSTR);
            break;
         case X_Data:
            X = (short)XDataPntr->z.x;
            Y = (short)XDataPntr->z.y;
            fwrite(&X,2,1,OUTSTR);
            fwrite(&Y,2,1,OUTSTR);
            fwrite(&F,2,1,OUTSTR);
            break;
         case TX_Data:
            T = (short)TXDataPntr->t;
            X = (short)TXDataPntr->z.x;
            Y = (short)TXDataPntr->z.y;
            fwrite(&T,2,1,OUTSTR);
            fwrite(&X,2,1,OUTSTR);
            fwrite(&Y,2,1,OUTSTR);
            break;
         default:
            Zencode(10,"DBCON   : Invalid File Type.\n");
            return(1);
         }
      if (ferror(OUTSTR)) return(1);
      }
   fputc(0x1A,OUTSTR);  /* ^Z signals EOF for BASIC */
   return(0);
   }

/*********************************************************************
**
** Standard Format to ASCII
*/
short STOA(WhatType)
short WhatType;
   {
   double X, Y, T;
   char *DLIM=", ";
   char *CRLF="\r\n";
   struct FILEHDR FH;

   if (!strlen(TFORMAT)) strcpy(TFORMAT,"%G");  /* Setup default output */
   if (!strlen(YFORMAT)) strcpy(YFORMAT,"%G");  /* formats if necessary */

   if (!Zgethead(INSTR,&FH)) return(1);
   while (Zread(INSTR,CONBUF,FH.type))  /* Read 1 value */
      {
      switch (FH.type)
         {
         case R_Data:
            Y = RDataPntr->y;
            T = TOTAL * FH.m + FH.b;
            if (WhatType)                 /* Write time data */
               {
               fprintf(OUTSTR,TFORMAT,T);
               fprintf(OUTSTR,DLIM);
               }
            fprintf(OUTSTR,YFORMAT,Y);
            break;
         case TR_Data:
            T = TRDataPntr->t;
            Y = TRDataPntr->y;
            fprintf(OUTSTR,TFORMAT,T);
            fprintf(OUTSTR,DLIM);
            fprintf(OUTSTR,YFORMAT,Y);
            break;
         case X_Data:
            X = XDataPntr->z.x;
            Y = XDataPntr->z.y;
            T = TOTAL * FH.m + FH.b;
            if (WhatType)               /* Write time data */
               {
               fprintf(OUTSTR,TFORMAT,T);
               fprintf(OUTSTR,DLIM);
               }
            fprintf(OUTSTR,YFORMAT,X);
            fprintf(OUTSTR,DLIM);
            fprintf(OUTSTR,YFORMAT,Y);
            break;
         case TX_Data:
            T = TXDataPntr->t;
            X = TXDataPntr->z.x;
            Y = TXDataPntr->z.y;
            fprintf(OUTSTR,TFORMAT,T);
            fprintf(OUTSTR,DLIM);
            fprintf(OUTSTR,YFORMAT,X);
            fprintf(OUTSTR,DLIM);
            fprintf(OUTSTR,YFORMAT,Y);
            break;
         default:
            Zencode(10,"DBCON   : Invalid File Type.\n");
            return(1);
         }
      ++TOTAL;
      fprintf(OUTSTR,CRLF);
      if (ferror(OUTSTR)) return(1);
      }
   return(0);
   }

/*********************************************************************
**
** Standard Format to 2 Byte Binary Integer
*/
short STOI()
   {
   short X, Y, T, F=0;
   struct FILEHDR FH;

   if (!Zgethead(INSTR,&FH)) return(1);
   while (Zread(INSTR,CONBUF,FH.type))
      {
      switch (FH.type)
         {
         case R_Data:
            Y = (short)RDataPntr->y;
            fwrite(&Y,2,1,OUTSTR);
            fwrite(&F,2,1,OUTSTR);
            break;
         case TR_Data:
            T = (short)TRDataPntr->t;
            Y = (short)TRDataPntr->y;
            fwrite(&T,2,1,OUTSTR);
            fwrite(&Y,2,1,OUTSTR);
            break;
         case X_Data:
            X = (short)XDataPntr->z.x;
            Y = (short)XDataPntr->z.y;
            fwrite(&X,2,1,OUTSTR);
            fwrite(&Y,2,1,OUTSTR);
            fwrite(&F,2,1,OUTSTR);
            break;
         case TX_Data:
            T = (short)TXDataPntr->t;
            X = (short)TXDataPntr->z.x;
            Y = (short)TXDataPntr->z.y;
            fwrite(&T,2,1,OUTSTR);
            fwrite(&X,2,1,OUTSTR);
            fwrite(&Y,2,1,OUTSTR);
            break;
         default:
            Zencode(10,"DBCON   : Invalid File Type.\n");
            return(1);
         }
      if (ferror(OUTSTR)) return(1);
      }
   return(0);
   }

/***********************************************************************
**
** Read an ASCII value.
** Anything other than a white space, digit, comma, period, e, E, - or +
** is considered and error and kills the task.
** ANY white space or comma is considered a delimeter.
** Both and EOF returns 0, ERROR -1, else 1 is returned.  The calling
** program must determine if a NULL return is an ERROR or EOPF.
*/
short READASCII(YP)
double *YP;
   {
   char *CPNTR;

   CPNTR = BUFFER;
   do *CPNTR = fgetc(INSTR);
   while (((*CPNTR<(char)33) || (*CPNTR == ',')) && (!feof(INSTR)));
   if (feof(INSTR) || ferror(INSTR)) return(0);
   do {
      *(++CPNTR) = fgetc(INSTR);
      if (ferror(INSTR))
         {
         Zerror();
         return(-1);
         }
      if ((!feof(INSTR))       &&
          (!isspace(*CPNTR))   &&
          (!isdigit(*CPNTR))   &&
          (*CPNTR != ',')      &&
          (*CPNTR != '.')      &&
          (*CPNTR != 'e')      &&
          (*CPNTR != 'E')      &&
          (*CPNTR != '-')      &&
          (*CPNTR != '+'))
            {
            Zencode(10,"DBCON   : Not an ASCII File\n");
            return(-1);
            }
      }
   while ((!feof(INSTR)) && (*CPNTR != (char)13) && (*CPNTR != ',')
       && (!isspace(*CPNTR)));
   *CPNTR = 0;
   *YP = atof(BUFFER);
   return(1);
   }

/*********************************************************************
**
** Read a 2 Byte integer value.
** EOF RETURN 1 or ERROR returns -1, else 0 is returned.
*/
short READINT(YP)
double *YP;
   {
   short Y;

   fread(&Y,2,1,INSTR);
   if (feof(INSTR)) return(0);
   if (ferror(INSTR))
      {
      Zerror();
      return(-1);
      }
   *YP = (double)Y;
   return(1);
   }

/***************************************************************
**
** Process ^C Interrupt
*/
short BREAKREQ()
   {
   fcloseall();
   unlink(TMPFILE);
   Zexit(3);
   }

/***************************************************************
**
** Trap Floating Point Error
*/
short FPEERROR()
   {
   fcloseall();
   unlink(TMPFILE);
   Zexit(1);
   }