/*********************************************************************
*
*  Task DBCMB
*
* I combine two data bases in a multitude of fashions.
* When combining files, the timing information is
* ignored and the time stamp from the primary is kept while that
* of the secondary file is lost.
* Note that for complex data bases, ZFACTOR is used as the complex FACTOR
*
* Currently only files of the same data type can be combined.
*
* CODE  0 -> DB1 + FACTOR * DB2
*       1 -> DB1 * DB2
*       2 -> DB1 / DB2
*       3 -> DB1 ^ DB2
*       4 -> LOG(DB1) BASE DB2
*       5 -> APPEND DB2 TO DB1
*       6 -> INTERLEAVE DB1 WITH DB2
*       7 -> CONVOLVE DB1 and DB2
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <memory.h>
#include <io.h>
#include <time.h>
#include <dos.h>
#include <process.h>
#include <ctype.h>
#include <signal.h>

#include "TISAN.H"

char TMPFILE[_MAX_PATH];           /* Must be visible to BREAKREQ */

main(argc,argv)
short argc;
char *argv[];
   {
   char BUFF1[sizeof(struct TXData)];
   char BUFF2[sizeof(struct TXData)];
   char BUFF[sizeof(struct TXData)];
   char INFILE[_MAX_PATH], OUTFILE[_MAX_PATH], IN2FILE[_MAX_PATH];
   char *DVZP = "DBCMB   : Division by Zero, Returned ZERO.\n";
   double Rval1, Rval2, Rval, Rsum;
   struct complex Zval1, Zval2, Zval, Zfactor, Zsum;
   FILE *INSTR, *OUTSTR, *IN2STR;
   struct FILEHDR F1Header, F2Header, FHeader;
   struct RData   *RDataPntr,  *RDataPntr1,  *RDataPntr2;
   short ERRFLAG=0;
   short FLAG1=0, FLAG2=0, FLAG;
   char *N1, *N2;
   struct TRData *TRDataPntr, *TRDataPntr1, *TRDataPntr2;
   struct TXData *TXDataPntr, *TXDataPntr1, *TXDataPntr2;
   struct XData  *XDataPntr,  *XDataPntr1,  *XDataPntr2;
   double R, THETA, LNR;
   long lF1Offset, lF2Offset, lF2len, lF1len, lF1Base, lF2Base, lOffset;

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

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

   if ((CODE < 0) || (CODE > 7))
      {
      Zencode(10,"DBCMB   : CODE Out of Range\n");
      Zexit(1);
      }

   ZBuildFileName(M_inname,INFILE);          /* Build file names */
   ZBuildFileName(M_in2name,IN2FILE);
   ZBuildFileName(M_outname,OUTFILE);
   ZBuildFileName(M_tmpname,TMPFILE);
/*
** Open input file and verify that we know what it is
*/
   Zencode(2,"DBCMB   : Opening Input File '%s'\n",INFILE);
   if (((INSTR = Zopen(INFILE,O_readb)) == NULL) ||
       (!Zgethead(INSTR,&F1Header))) Zexit(1);

   switch (F1Header.type)
      {
      case R_Data:
      case TR_Data:
      case X_Data:
      case TX_Data:
         break;
      default:
         Zencode(10,"DBCMB   : Invalid file type.\n");
         Zexit(1);
      }
/*
** Open second input file and verify that we know what it is
*/
   Zencode(2,"DBCMB   : Opening Secondary Input File '%s'\n",IN2FILE);
   if (((IN2STR = Zopen(IN2FILE,O_readb)) == NULL) ||
       (!Zgethead(IN2STR,&F2Header))) Zexit(1);

   switch (F2Header.type)
      {
      case R_Data:
      case TR_Data:
      case X_Data:
      case TX_Data:
         break;
      default:
         Zencode(10,"DBCMB   : Invalid file type.\n");
         Zexit(1);
      }

   if (F1Header.type != F2Header.type)
      {
      Zencode(10,"DBCMB   : Cannot combine files of different types.\n");
      Zexit(1);
      }

   if ((F1Header.m != F2Header.m) || (F1Header.m != F2Header.m))
      Zencode(8,"DBCMB   : ** WARNING ** Different Time Scales!\n");
/*
** Currently, the file takes on the type of file #1.
**
** Envntually, it will take on the type of the largest
** data structure used.
*/
   FHeader = F1Header;
   Zfactor.x = ZFACTOR[0];
   Zfactor.y = ZFACTOR[1];
   Zencode(2,"DBCMB   : Opening Scratch File '%s'\n",TMPFILE);
   if (((OUTSTR = Zopen(TMPFILE,O_writeb)) == NULL) ||
       Zputhead(OUTSTR,&FHeader))
      {
      ERRFLAG = 1;
      goto EXIT;
      }

   RDataPntr  =  (struct  RData *)BUFF;
   RDataPntr1  = (struct  RData *)BUFF1;
   RDataPntr2  = (struct  RData *)BUFF2;
   TRDataPntr =  (struct TRData *)BUFF;
   TRDataPntr1 = (struct TRData *)BUFF1;
   TRDataPntr2 = (struct TRData *)BUFF2;
   XDataPntr  =  (struct  XData *)BUFF;
   XDataPntr1  = (struct  XData *)BUFF1;
   XDataPntr2  = (struct  XData *)BUFF2;
   TXDataPntr =  (struct TXData *)BUFF;
   TXDataPntr1 = (struct TXData *)BUFF1;
   TXDataPntr2 = (struct TXData *)BUFF2;

   switch (CODE)           /* Select Action */
      {
      case 5:         /* Append DB2 to DB1 */
         while (Zread(INSTR,BUFF1,F1Header.type))
            {
            if (Zwrite(OUTSTR,BUFF1,F1Header.type))
               {
               ERRFLAG=1;
               goto EXIT;
               }
            }
         if (ferror(INSTR))
            {
            ERRFLAG=1;
            goto EXIT;
            }
         while (Zread(IN2STR,BUFF2,F2Header.type))
            {
            if (Zwrite(OUTSTR,BUFF2,F2Header.type))
               {
               ERRFLAG=1;
               goto EXIT;
               }
            }
         
         if (ferror(INSTR)) ERRFLAG=1;
         goto EXIT;
         break;

      case 6:     /* Inter Leave */
         if (F1Header.type != R_Data)
           {
           Zencode(10,"DBCMB   : Invalid File Type for Interleave\n");
           ERRFLAG = 1;
           goto EXIT;
           }
         FHeader.type = TR_Data;
         if (Zputhead(OUTSTR,&FHeader))
            {
            ERRFLAG = 1;
            goto EXIT;
            }
         while ((N1 = Zread(INSTR,BUFF1,R_Data)) &&
                (N2 = Zread(IN2STR,BUFF2,R_Data)))
            {
            TRDataPntr->y = RDataPntr1->y;
            TRDataPntr->t = RDataPntr2->y;
            if (Zwrite(OUTSTR,BUFF,TR_Data))
               {
               ERRFLAG = 1;
               goto EXIT;
               }
            }
         if (ferror(INSTR))
            {
            ERRFLAG=1;
            goto EXIT;
            }
         if (!N1) N2 = Zread(IN2STR,BUFF2,F2Header.type);
         if (N1 != N2)     /* If both N1 and N2 are not NULL then message */
            Zencode(8,"DBCMB   : WARNING!!! Files are of Different Lengths\n");
         goto EXIT;
      case 7:  /* Convolve */
         lF1len = filelength(fileno(INSTR)) - 1;
         lF2len = filelength(fileno(IN2STR)) - 1;
         lF1Offset = (lF1Base = ftell(INSTR)) - Zsize(F1Header.type);
         lF2Base = lF2Offset = ftell(IN2STR);
         while (lF2Offset < lF2len)
            {
            lF1Offset += Zsize(F1Header.type);
            if (lF1Offset >= lF1len)
               {
               lF2Offset += Zsize(F2Header.type);
               lF1Offset -= Zsize(F1Header.type);
               if ((F1Header.type == TR_Data) ||
                   (F1Header.type == TR_Data)) break;
               }
            lOffset = lF1Offset;
            Rsum = Zsum.x = Zsum.y = 0.;
            fseek(IN2STR, lF2Offset, SEEK_SET);
            for ( fseek(INSTR, lF1Offset, SEEK_SET),
                  Zread(INSTR,  BUFF1, F1Header.type),
                  Zread(IN2STR, BUFF2, F2Header.type);
                    !feof(INSTR)  &&
                    !feof(IN2STR) &&
                    ((lOffset -= Zsize(F1Header.type)) >= lF1Base);
                  fseek(INSTR, lOffset, SEEK_SET),
                  Zread(INSTR,  BUFF1, F1Header.type),
                  Zread(IN2STR, BUFF2, F2Header.type))
               {
               switch (F1Header.type)
                  {
                  case R_Data:
                     Rval1 = RDataPntr1->y;
                     FLAG1 = RDataPntr1->f;
                     break;
                  case TR_Data:
                     Rval1 = TRDataPntr1->y;
                     break;
                  case X_Data:
                     Zval1 = XDataPntr1->z;
                     FLAG1 = XDataPntr1->f;
                     break;
                  case TX_Data:
                     Zval1 = TXDataPntr1->z;
                     break;
                  }
               switch (F2Header.type) /* Get data from input file #2 */
                  {
                  case R_Data:
                     Rval2 = RDataPntr2->y;
                     FLAG2 = RDataPntr2->f;
                     break;
                  case TR_Data:
                     Rval2 = TRDataPntr2->y;
                     break;
                  case X_Data:
                     Zval2 = XDataPntr2->z;
                     FLAG2 = XDataPntr2->f;
                     break;
                  case TX_Data:
                     Zval2 = TXDataPntr2->z;
                     break;
                  }
               switch (FHeader.type) /* Set data for output file */
                  {
                  case R_Data:
                     FLAG = FLAG1 | FLAG2;
                  case TR_Data:
                     if (!FLAG) Rsum += (Rval1 * Rval2);
                     break;
                  case X_Data:
                     FLAG = FLAG1 | FLAG2;
                  case TX_Data:
                     if (!FLAG) 
                        {
                        Zval = c_mul(Zval1, Zval2);
                        Zsum = c_add(Zsum, Zval);
                        }
                     break;
                  }
               }

            switch (FHeader.type)  /* Output Data */
               {
               case R_Data:
                  RDataPntr->y = Rsum;
                  RDataPntr->f = 0;
                  break;
               case TR_Data:
                  TRDataPntr->t = TRDataPntr1->t;
                  TRDataPntr->y = Rsum;
                  break;
               case X_Data:
                  XDataPntr->z = Zsum;
                  XDataPntr->f = 0;
                  break;
               case TX_Data:
                  TXDataPntr->t = TXDataPntr1->t;
                  TXDataPntr->z = Zsum;
                  break;
               }
            if (Zwrite(OUTSTR,BUFF,FHeader.type))
               {
               ERRFLAG = 1;
               break;
               }
            } /* End While */
         goto EXIT;
      } /* End SWITCH over 5, 6 and 7*/

/*
** Note:  The data processing program structure is written
**        in such a way that the modifications needed for
**        combining un-like data files can be more easyly
**        implemented.
*/
   while ((N1 = Zread(INSTR,BUFF1,F1Header.type)) &&
          (N2 = Zread(IN2STR,BUFF2,F2Header.type)))
      {
      switch (F1Header.type) /* Get data from input file #1 */
         {
         case R_Data:
            Rval1 = RDataPntr1->y;
            FLAG1 = RDataPntr1->f;
            break;
         case TR_Data:
            Rval1 = TRDataPntr1->y;
            break;
         case X_Data:
            Zval1 = XDataPntr1->z;
            FLAG1 = XDataPntr1->f;
            break;
         case TX_Data:
            Zval1 = TXDataPntr1->z;
            break;
         }
      switch (F2Header.type) /* Get data from input file #2 */
         {
         case R_Data:
            Rval2 = RDataPntr2->y;
            FLAG2 = RDataPntr2->f;
            break;
         case TR_Data:
            Rval2 = TRDataPntr2->y;
            break;
         case X_Data:
            Zval2 = XDataPntr2->z;
            FLAG2 = XDataPntr2->f;
            break;
         case TX_Data:
            Zval2 = TXDataPntr2->z;
            break;
         }
      FLAG = Max(FLAG1, FLAG2);
      if (!FLAG)   /* Only process valid data points */
         {
         switch (CODE)
            {
            case 0: /* DB1 + FACTOR * DB2 */
               switch(FHeader.type)
                  {
                  case X_Data:
                  case TX_Data:
                     Zval = c_add(Zval1,c_mul(Zval2,Zfactor));
                     break;
                  case R_Data:
                  case TR_Data:
                     Rval = Rval1 + FACTOR*Rval2;
                     break;
                  }
               break;
            case 1: /* DB1 * DB2 */
               switch(FHeader.type)
                  {
                  case X_Data:
                  case TX_Data:
                     Zval = c_mul(Zval1,Zval2);
                     break;
                  case R_Data:
                  case TR_Data:
                     Rval = Rval1 * Rval2;
                     break;
                  }
               break;
            case 2: /* DB1 / DB2 */
               switch(FHeader.type)
                  {
                  case X_Data:
                  case TX_Data:
                     if (!cabs(Zval2))
                        {
                        Zencode(8,DVZP);
                        Zval.x = Zval.y = 0.;
                        }
                     else
                        Zval = c_div(Zval1,Zval2);
                     break;
                  case R_Data:
                  case TR_Data:
                     if (!Rval2)
                        {
                        Zencode(8,DVZP);
                        Rval = 0.;
                        }
                     else
                        Rval = Rval1 / Rval2;
                     break;
                  }
               break;
            case 3: /* POW (DB1,DB2) */
               switch(FHeader.type)
                  {
                  case X_Data:
                  case TX_Data:
                     Zval = c_pow(Zval1,Zval2);
                     break;
                  case R_Data:
                  case TR_Data:
                     Rval = pow(Rval1,Rval2);
                     break;
                  }
               break;
            case 4: /* LOG(DB1) BASE DB2 */
               switch(FHeader.type)
                  {
                  case X_Data:
                  case TX_Data:
                     Zval = c_ln(Zval2);
                     if  (!cabs(Zval))
                        {
                        Zencode(8,DVZP);
                        Zval.x = Zval.y = 0.;
                        }
                     else
                        Zval = c_div(c_ln(Zval1),Zval);
                     break;
                  case R_Data:
                  case TR_Data:
                     Rval = log(Rval2);
                     if (!Rval)
                        {
                        Zencode(8,DVZP);
                        Rval = 0.;
                        }
                     else
                        Rval = log(Rval1)/Rval;
                     break;
                  }
               break;
            }
         }

      switch (F1Header.type)  /* Output Data */
         {
         case R_Data:
            RDataPntr->y = Rval;
            RDataPntr->f = FLAG;
            break;
         case TR_Data:
            TRDataPntr->t = TRDataPntr1->t;
            TRDataPntr->y = Rval;
            break;
         case X_Data:
            XDataPntr->z = Zval;
            XDataPntr->f = FLAG;
            break;
         case TX_Data:
            TXDataPntr->t = TXDataPntr1->t;
            TXDataPntr->z = Zval;
            break;
         }
      if (Zwrite(OUTSTR,BUFF,F1Header.type))
         {
         ERRFLAG = 1;
         break;
         }
      } /* End WHILE */

   if (ferror(INSTR)) ERRFLAG=1;

/* If both N1 and N2 are not NULL then message */
   if (!N1) N2 = Zread(IN2STR,BUFF2,F2Header.type);
   if (!ERRFLAG && (N1 != N2))
      Zencode(8,"DBCMB   : WARNING!!! Files are of Different Lengths\n");

EXIT:
   Zclose(INSTR);
   Zclose(IN2STR);
   Zclose(OUTSTR);
   ERRFLAG = Znameout(OUTFILE,TMPFILE,ERRFLAG);
   Zexit(ERRFLAG);
   }

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