/*********************************************************************
*
*  Task IMEAN
*
*  Task to display basic statistics on a database, with the option
*  to return information in the IMEAN.INP file.
*
*  CODE 0 (default) -> No Value Returned
*  CODE 1 -> FACTOR = -DataMean, ZFACTOR = ComplexMean
*  CODE 2 -> FACTOR = LOWVAL, ZFACTOR = ComplexMin
*            TRANGE = T +/- 1 Point
*  CODE 3 -> FACTOR = HIGHVAL, ZFACTOR = ComplexMax
*            TRANGE = T +/- 1 Point
*
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <io.h>
#include <time.h>
#include <dos.h>
#include <process.h>
#include <ctype.h>
#include <signal.h>

#include "TISAN.H"

short FPERROR(void);
void BombOff(int);

struct FILESTRUCT File;

main(argc,argv)
short argc;
char *argv[];
   {
   struct FILEHDR FileHeader;
   double TimeCount=0.,DataTime, DataCount=0.;
   double FlaggedCount=0., DataCountInRange=0.;
   double FileDataMin , FileDataMax , RangeDataMin , RangeDataMax , Data;
   double FileStartTime , RangeTimeOfMax , RangeTimeOfMin;
   double RangeCountAtMin, RangeCountAtMax, DataSum=0., SumSquared=0.;
   double DataMean, SIGMA;
   double TLLAST, TLNEXT, THLAST, THNEXT, LastDataTime;
   short FLAG=0;
   BOOL bFileFirstPass = TRUE, bRangeFirstPass = TRUE;
   BOOL bMinJustFound, bMaxJustFound;
   int iErrorReturn=0;
   char szFileName[_MAX_PATH];
   char DataBuffer[sizeof(struct TXData)];  /* Dimension to largest data size */
   struct RData  *RDataPntr;
   struct TRData *TRDataPntr;
   struct XData  *XDataPntr;
   struct TXData *TXDataPntr;
   struct complex ComplexMean, ComplexMin, ComplexMax, ComplexSum, ComplexData;

   signal(SIGINT,BREAKREQ);        /* Set ^C Interrupt Vector */
   signal(SIGFPE,FPERROR);  /* Setup Floating Point Error Trap */

   if (zTaskInit("IMEAN",argv[0])) Zexit(1); /* Initialize Task IMEAN */

   ZBuildFileName(M_inname,szFileName);      /* Build an input file name */
/*
** Open the input file and read in the file header.
** Task dies if either opertation returns an error.
*/
   Zencode(2,"IMEAN   : Opening Input File '%s'\n",szFileName);
   if (zFileOpen(&File, szFileName, OF_READ, 0)) Zexit(1);
   if (!zGetHeader(&File,&FileHeader)) BombOff(1);

   RDataPntr =  (struct RData *)DataBuffer;
   TRDataPntr = (struct TRData *)DataBuffer;
   XDataPntr =  (struct XData *)DataBuffer;
   TXDataPntr = (struct TXData *)DataBuffer;

   ComplexSum = cmplx(0., 0.);

   while (!zIndexedRead(&File, -1L, DataBuffer, FileHeader.type))
      {
      switch (FileHeader.type)  /* Fill Data, DataTime and FLAG values */
         {
         case R_Data:    /* Real Time Series */
            Data = RDataPntr->y;
            DataTime = TimeCount*FileHeader.m + FileHeader.b; /* Time by position */
            FLAG =RDataPntr->f;
            break;
         case TR_Data:  /* Real Time Labeled */
            Data = TRDataPntr->y;
            DataTime = TRDataPntr->t;
            break;
         case X_Data:   /* Complex Time Series */
            Data = cabs(ComplexData = XDataPntr->z);
            DataTime = TimeCount*FileHeader.m + FileHeader.b; /* Time by position */
            FLAG =XDataPntr->f;
            break;
         case TX_Data:   /* Complex Time Labeled */
            Data = cabs(ComplexData = TXDataPntr->z);
            DataTime = TXDataPntr->t;
            break;
         default:  /* File type is not known */
            Zencode(10,"IMEAN   : Invalid File Type.\n");
            BombOff(1);
         }

      if (FLAG)      /* is data valid ?                    */
         ++FlaggedCount;   /* If not, then count # of bad points */
      else           /* Data is good so do statistics      */
         {
         ++DataCount;   /* Numer of valid points */
         if (bFileFirstPass) /* First pass for file MIN/MAX scan */
            {
            FileDataMin = FileDataMax = Data; /* Initialize Data Max and Min values */
            LastDataTime = FileStartTime = DataTime;  /* Time range of file (minimum) */
            bFileFirstPass = FALSE;           /* Initialization flag true */
            }
         else
            {
            FileDataMin = Min(FileDataMin,Data);  /* Find minimum value in file */
            FileDataMax = Max(FileDataMax,Data);  /* Find maximum value in file */
            }
/*
** Only data within the selected time range is processed.
** If TRANGE[0] >= TRANGE[1] then all the data is used
*/
         if ((((DataTime <= TRANGE[1]) && (DataTime >= TRANGE[0])) ||
               (TRANGE[0] >= TRANGE[1])))
            {
            switch (FileHeader.type)
               {
               case X_Data:   /* Complex Time Series */
               case TX_Data:   /* Complex Time Labeled */
                  ComplexSum = c_add(ComplexSum,ComplexData);
               }
            ++DataCountInRange;
            DataSum += Data;            /* Sum of values   */
            SumSquared += Square(Data);   /* Sums of squares */
            if (bRangeFirstPass)      /* First pass for range MIN/MAX scan */
               {
               RangeDataMin = RangeDataMax = Data;
               ComplexMin = ComplexMax = ComplexData;
               TLLAST = TLNEXT = THLAST = THNEXT = RangeTimeOfMax = RangeTimeOfMin = DataTime;
               RangeCountAtMax = RangeCountAtMin = TimeCount;
               bMinJustFound = bMaxJustFound =  bRangeFirstPass = FALSE;
               }
            else
               {
               if (bMinJustFound)       /* Minimum for selected range was found   */
                  {              /* on the last pass, so store "NEXT Time" */
                  bMinJustFound = FALSE;
                  TLNEXT = DataTime;
                  }
               if (bMaxJustFound)       /* Maximum for selected range was found   */
                  {               /* on the last pass, so store "NEXT Time" */
                  bMaxJustFound = FALSE;
                  THNEXT = DataTime;
                  }
               if (RangeDataMax < Data)   /* New Maximum value */
                  {
                  RangeDataMax = Data;    /* Range value max */
                  ComplexMax = ComplexData;
                  RangeTimeOfMax = DataTime;      /* Time of maximum */
                  RangeCountAtMax = TimeCount;      /* File entry number */
                  THLAST = LastDataTime; /* Save Time of Previous Point */
                  bMaxJustFound = TRUE;
                  }
               if (RangeDataMin > Data)   /* New Minimum values */
                  {
                  RangeDataMin = Data;    /* Range value min */
                  ComplexMin = ComplexData;
                  RangeTimeOfMin = DataTime;      /* Time of minimum */
                  RangeCountAtMin = TimeCount;      /* File entry number */
                  TLLAST = LastDataTime; /* Save Time of Previous Point */
                  bMinJustFound = TRUE;
                  }
               }
            }
         LastDataTime = DataTime;  /* Keep this DataTime for the next iteration */
         }
      ++TimeCount;        /* Count Number of Reads */
      } /* end while */

   if (File.iEoF < 0)
      iErrorReturn = 1;
   else
      {
      if (!strlen(TFORMAT)) strcpy(TFORMAT,"%G");  /* Setup default output */
      if (!strlen(YFORMAT)) strcpy(YFORMAT,"%G");  /* formats if needed.   */
      Zencode(3,"IMEAN   : ");
      switch(FileHeader.type)  /* Print File Type */
         {
         case R_Data:
            Zencode(3,"Real Time Series File\n");
            break;
         case X_Data:
            Zencode(3,"Complex Time Series File\n");
            break;
         case TR_Data:
            Zencode(3,"Real Time Stamped File\n");
            break;
         case TX_Data:
            Zencode(3,"Complex Time Stamped File\n");
            break;
         }
      Zencode(3,"IMEAN   : File Contains %G Valid Points\n",DataCount);
      Zencode(3,"IMEAN   : File Contains %G Flagged Points\n",FlaggedCount);
      Zencode(3,"IMEAN   : File Contains %G Points in Selected Range\n",DataCountInRange);

      if (DataCountInRange > 1.)
         SIGMA = ((DataCountInRange * SumSquared) - Square(DataSum))/(DataCountInRange*(DataCountInRange-1.));
      SIGMA = sqrt(Max(0.,SIGMA));
      if (DataCountInRange > 0.)
         {
         DataMean = DataSum/DataCountInRange;
         ComplexMean.x = ComplexSum.x / DataCountInRange;
         ComplexMean.y = ComplexSum.y / DataCountInRange;
         }

      Zencode(3,"IMEAN   : Full Time Range of File ");
      Zencode(3,TFORMAT,FileStartTime);
      Zencode(3,", ");
      Zencode(3,TFORMAT,DataTime);
      Zencode(3,"\n");
      Zencode(3,"IMEAN   : Amplitude Range of File ");
      Zencode(3,YFORMAT,FileDataMin);
      Zencode(3,", ");
      Zencode(3,YFORMAT,FileDataMax);
      Zencode(3,"\n");
      Zencode(5,"IMEAN   : Maximum Value in Selected Range ");
      Zencode(5,YFORMAT,RangeDataMax);
      Zencode(5,"\n");
      if ((FileHeader.type == X_Data) || (FileHeader.type == X_Data))
         Zencode(5,"IMEAN   : Z = %G + i(%G)\n",ComplexMax.x,ComplexMax.y);
      Zencode(4,"IMEAN   : at Entry %G\n",RangeCountAtMax);
      Zencode(5,"IMEAN   : Corresponding to Time ");
      Zencode(5,TFORMAT,RangeTimeOfMax);
      Zencode(5,"\n");
      Zencode(5,"IMEAN   : Minimum Value in Selected Range ");
      Zencode(5,YFORMAT,RangeDataMin);
      Zencode(5,"\n");
      if ((FileHeader.type == X_Data) || (FileHeader.type == X_Data))
         Zencode(5,"IMEAN   : Z = %G + i(%G)\n",ComplexMin.x,ComplexMin.y);
      Zencode(4,"IMEAN   : at Entry %G\n",RangeCountAtMin);
      Zencode(5,"IMEAN   : Corresponding to Time ");
      Zencode(5,TFORMAT,RangeTimeOfMin);
      Zencode(5,"\n");
      Zencode(6,"IMEAN   : Mean Amplitude Value of ");
      Zencode(6,YFORMAT,DataMean);
      Zencode(6,"\n");
      if ((FileHeader.type == X_Data) || (FileHeader.type == X_Data))
         Zencode(6,"IMEAN   : Z = %G + i(%G)\n",ComplexMean.x,ComplexMean.y);
      Zencode(6,"IMEAN   : Standard Deviation of ");
      Zencode(6,YFORMAT,SIGMA);
      Zencode(6,"\n");

      if (!zGetAdverbs("IMEAN")) /* Restore adverbs */
         {
         switch (CODE)           /* Return selected value (if any) */
            {
            case 1:
               if ((FileHeader.type == X_Data) || (FileHeader.type == X_Data))
                  {
                  ZFACTOR[0] = -ComplexMean.x;
                  ZFACTOR[1] = -ComplexMean.y;
                  }
               FACTOR = -DataMean;
               break;
            case 2:
               TRANGE[0] = TLLAST;
               TRANGE[1] = TLNEXT;
               FACTOR = RangeDataMin;
               if ((FileHeader.type == X_Data) || (FileHeader.type == X_Data))
                  {
                  ZFACTOR[0] = ComplexMin.x;
                  ZFACTOR[1] = ComplexMin.y;
                  }
               break;
            case 3:
               TRANGE[0] = THLAST;
               TRANGE[1] = THNEXT;
               FACTOR = RangeDataMax;
               if ((FileHeader.type == X_Data) || (FileHeader.type == X_Data))
                  {
                  ZFACTOR[0] = ComplexMax.x;
                  ZFACTOR[1] = ComplexMax.y;
                  }
               break;
            }
         if (zPutAdverbs("IMEAN")) iErrorReturn = 1;
         }
      else
         iErrorReturn = 1;
      }

   iErrorReturn = zFileClose(&File, TRUE) ? 1 : iErrorReturn;
   Zexit(iErrorReturn);
   }

/***************************************************************
**
** Process ^C Interrupt
*/
short BREAKREQ()
   {
   zFileClose(&File, TRUE);
   Zexit(3);
   }

short FPERROR()
   {
   zFileClose(&File, TRUE);
   Zexit(1);
   }

void BombOff(i)
int i;
   {
   zFileClose(&File, TRUE);
   Zexit(i);
   }