/***********************************************************************
                   B P   A M E R I C A  
           PROPRIETARY - TO BE MAINTAINED IN CONFIDENCE 
                         COPYRIGHTED 2006
 ***********************************************************************

                           TEMPLATE 3

     "C" template demonstrating the use of dds convenience 
     routines for multiple input and output datasets using 
     referencing input headers and creating new output headers.
     this also includes an optional input dataset and reading a 
     full volume dataset.

     This may have become pretty busy trying to show several different 
     techniques in this one template.  

     Written by Jerry Ehlers November 2006

 **********************************************************************/

#define _POSIX_SOURCE 1    /* Check POSIX Standard            */
#define ANSI               /* Turn on prototyping from cdds.h */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
/*
   INCLUDE THE DDS API (Application Program Interface)
 */
#include <cdds.h>           /* "C" dds include */

#define RCSID "$Id: c_template3.html 64 2009-03-04 21:24:41Z ehlersjw $"
#define TITLE "template3: C template using DDS convenience routines"

void doit(int, int, int, int, int, int, int, int, int, int, int, int,
          int, int, int, int, float, float, float, float, float, float,
          float, float, float, float, float, float, float, float, float, 
          float*, float*, float*, float*, char*);

/***********************************************************************
 *
 * main
 *
 **********************************************************************/
int main(int argc, char **argv)
{
   BIN_TAG in_bin=-1, aux_bin=-1, out_bin=-1;
   int ier, ibeta, n1_out, nh, nbytes, nwrds;
   int n1, n2, n3, n4, n1_aux, n2_aux, n3_aux, n1_vel, n2_vel, n3_vel;
   int rank, insmpl, outsmpl;
   int vsize[RANK_MAX];
   int check;
   float scale, d1_out, dh;
   float d1, d2, d3, d1_vel, d2_vel, d3_vel;
   float o1, o2, o3, o1_vel, o2_vel, o3_vel;
   float vdelta[RANK_MAX], vorigin[RANK_MAX];
   float *indata, *auxdata, *vel, *outdata;
   char genus[16], ctype[16], axis[3][AXISNAME_MAX];
   char *prog;

   /***********************************************************************
    *    initialize
    **********************************************************************/
   /*
    * get the program name
    */
   prog = strrchr(argv[0], '/');
   if (prog) prog++;
   else prog = argv[0];

   /*
    * pass the command line arguments on to DDS
    */
   setargcv(argc, argv);
   /*
      OPEN THE PRINT FILE & CHECK FOR HELP 
      'cdds_openpr' function:
           Open a printfile using the information automatically 
           generated by RCS in '$Id: c_template3.html 64 2009-03-04 21:24:41Z ehlersjw $'.  If the user specifies some
           form of help (-h help= HELP=...) on the command line,
           then the return value will be < 0.
    */
   ier = cdds_openpr(prog, RCSID);
   if (ier > 0) help();

   /***********************************************************************
    *    open input data file
    **********************************************************************/
   /*    
      OPEN THE INPUT DATASET 
      'cddx_inhdr' function:
           This function opens the input file, with the specified 
           headers as integers in the trace buffer followed by the
           other input headers and then the data Samples as real values.
           The user can override the input format and binary data by  
           defining "in_format=" and⁄or "in_data=" since the first 
           argument is 'in'. If no headers are to be used yet you 
           still want to pass them to an output file, use cddx_in2.  
           If you simply want to read the Samples and throw away any 
           input headers, use cddx_in.
    */
   in_bin = cddx_inhdr("in", "stdin:", TITLE, DDS_FLOAT, DDS_FLOAT,
                     "SrPtXC SrPtYC StaCor");
   /*
      PRINT ERROR MESSAGE
      'cdds_prterr' function:
           Print an error message to the console (& printfile if 
           exists). Error count is kept by DDS.  Check for errors later,  
           before processing to check for as many things as possible first.
    */
   if (in_bin < 0) {
      cdds_prterr("Unable to open input data\n");
   }
   /*
      RETRIEVE DATASET DEFINITIONS NEEDED BY APPLICATION 
      'cdds_scank' function:
           With ' ' as the second parameter, just return the number
           of axes defined.

      'cdds_scanf' function:
           Default values are assigned prior to definition retrieval.
           A variable are unchanged, if it is not specified.

           NOTE: What's special about the "size.axis(1)" definition?
           'axis(N)' is automatically converted to the Nth axis name.
           This allows hyper cube attributes to be retrieved by axis
           number, instead of axis name.  For example, "size.axis(1)"
           becomes "size.t" (assuming "axis=  t x y").

           WARNING: Do NOT use %i, use %d to scan for integers because
                    %i will interpret a value with a leading "0" as 
                    octal instead decimal (eg. 010 would be read as
                    8 instead of 10).
    */
   /*
    *    Get axis names if needed later
    */
   cdds_scanf("axis", "%s %s %s", axis[0], axis[1], axis[2]);

   rank = cdds_scank("axis", "");
   n4 = 1;
   cdds_scanf("size.axis(1)", "%d", &n1);
   cdds_scanf("size.axis(2)", "%d", &n2);
   cdds_scanf("size.axis(3)", "%d", &n3);
   cdds_scanf("size.axis(4)", "%d", &n4);
   cdds_scanf("delta.axis(1)", "%f", &d1);
   cdds_scanf("delta.axis(2)", "%f", &d2);
   cdds_scanf("delta.axis(3)", "%f", &d3);
   cdds_scanf("origin.axis(1)", "%f", &o1);
   cdds_scanf("origin.axis(2)", "%f", &o2);
   cdds_scanf("origin.axis(3)", "%f", &o3);

   if (rank < 3 || rank > 4) {
      cdds_prterr("Can only handle 3D or 4D datasets in this program\n");
   }
   /*
      RETRIEVE INDEX OFFSET TO THE SAMPLES 
      'cddx_index' function:
           This function returns an offset index to the input Samples
           in each trace buffer.
    */
   tag = cdds_member(in_bin, 0, "Samples");
   genus = cdds_genus(in_bin, tag);

   insmpl = cddx_index(in_bin, "Samples", DDS_FLOAT);
   if (strcmp(genus,"complex")) {
      insmpl = 2 * insmpl;
   }

   /***********************************************************************
    *    open optional auxillary input file
    **********************************************************************/
   /*    
      OPEN THE OPTIONAL INPUT FILE 
      'cddx_in' function:
           This function opens an input file (throwing away any input
           trace headers) and without specifying a default filename (2nd
           argument) will simply return -2 without any errors.
    */
   aux_bin = cddx_in("aux", " ", TITLE);
   if (aux_bin >= 0) {
      cdds_scanf("size.axis(1)", "%d", &n1_aux);
      cdds_scanf("size.axis(2)", "%d", &n2_aux);
      cdds_scanf("size.axis(3)", "%d", &n3_aux);
      if (n1_aux != n1 || n2_aux != n2 || n3_aux != n3) {
         cdds_prterr("\"aux\" input dataset not conformable with \"in\"\n");
      }
   } else if (aux_bin != -2) {
      cdds_prterr("Unable to open aux input\n");
   }

   /***********************************************************************
    *    open and read velocity input file
    **********************************************************************/
   /*    
      OPEN AND READ THE VELOCITY INPUT FILE 
      'cddx_readall' function:
           This function opens the an file, allocates the buffer, reads
           the entire dataset, and closes the file.  This is convenient
           when you need to have an entire dataset in memory.  Just be
           aware that the program could abort if it is unable to 
           allocate the required memory.  
    */
   ier = cddx_readall("in", &vel, &rank, vsize, vdelta, vorigin);
   if (ier <= 0) {
      cdds_prterr("Unable to open \"vel\" dataset\n");
   } else if (rank != 3 && n2_vel*n3_vel != 1) {
      cdds_prterr(
     *        "\"vel\" must be 3D or a single velocity function\n");
   }

   n1_vel = vsize[0];
   n2_vel = vsize[1];
   n3_vel = vsize[2];
   d1_vel = vdelta[0];
   d2_vel = vdelta[1];
   d3_vel = vdelta[2];
   o1_vel = vorigin[0];
   o2_vel = vorigin[1];
   o3_vel = vorigin[2];

   /***********************************************************************
    *    read parameters
    **********************************************************************/
   /*
      RETRIEVE COMMAND LINE PARAMETERS
      'cdds_dict' function:
           'cdds_dict' selects the 'par:' dictionary for scanning.
           This dictionary only contains definitions from the command
           line, and parameter dictionaries ("par=  fn1  fn2 ...").
           Parameters ("in= ", "out_format= ", etc.) for the current
           process can be read, without ambiguity from the input history 
           (ie. local parameters only).
    */
   cdds_dict("par:", "scan");

   /*
    * get beta, scale and ctype
    * (initialize ibeta & scale defaults; ctype is required)
    */
   ibeta = 7;
   scale = 1.0;
   ctype[0] ='\0';
   cdds_scanf("beta", "%d", &ibeta);
   cdds_scanf("scale", "%f", &scale);
   cdds_scanf("type", "%s", ctype);
   if (ier < 0) {
      cdds_prterr("\"type\" MUST be specified\n");
   }

   /*
    * get output file parameters
    */
   n1_out = n1;
   d1_out = d1;
   nh = 1;
   dh = 50.0;
   cdds_scanf("n1_out", "%d", &n1_out);
   cdds_scanf("d1_out", "%f", &d1_out);
   cdds_scanf("nh", "%d", &nh);
   cdds_scanf("dh", "%f", &dh);
   /*
      RETRIEVE SWITCH PARAMETER
      'cdds_switch' function:
           This function returns non-zero if specified with nothing or
           "1 y Y yes Yes YES t T true True TRUE"; zero if specified 
           with "0 n N no No NO f F false False FALSE"; otherwise it
           returns the default value (2nd argument).
    */
   check = cdds_switch("check", 0);   

   /***********************************************************************
    *    print user parameters
    **********************************************************************/
   /*
      PRINT MESSAGES 
      'cdds_prtmsg' function:
           This function prints a formatted message to the print file
           opened by "cdds_openpr"; otherwise to the console (stderr).

      'cdds_prtcon' function:
           This function prints a formatted message to the console and
           to the print file if opened.
   
      'cdds_prtmsg' function:
           This function prints a formatted error message to the console 
           and to the print file if opened.  It also keeps track of the
           number of error messages printed for "cdds_closepr".
    */
   cdds_prtmsg("\n*** USER PARAMETERS ***\n\n");
   cdds_prtmsg("\tbeta   =%d\n", ibeta);
   cdds_prtmsg("\tscale  =%g\n", scale);
   cdds_prtmsg("\ttype   =%s\n", ctype);
   cdds_prtmsg("\tn1_out =%d\n", n1_out);
   cdds_prtmsg("\td1_out =%g\n", d1_out);
   cdds_prtmsg("\tnh     =%d\n", nh);
   cdds_prtmsg("\tdh     =%g\n", dh);
   if (check) {
      cdds_prtmsg("\tcheck  =YES\n");
   } else {
      cdds_prtmsg("\tcheck  =NO\n");
   }
   cdds_prtmsg("\n");

   /***********************************************************************
    *    allocate dynamic arrays
    **********************************************************************/
   /*
      Don't allocate arrays if there are already any errors; 
      we could have bad array sizes
    */ 
   if (cdds_errors()) goto finish;
   /*
      ALLOCATE DYNAMIC ARRAYS 
      'cdds_prec' function:
           This function returns the number of bytes for each trace in
           an open binary.

      'cdds_malloc' function:
           This function allocates memory or reports an error & aborts.
           Storage can be released by calling "cdds_free".  Use 
           "dds_debug= dbg_call" will cause DDS to check memory at
           each api call.
   */
   nbytes = cdds_prec(in_bin, 0);
   nwrds = nbytes/sizeof(float);
   indata = cdds_malloc(n2 * nbytes);
   nbytes = cdds_prec(out_bin, 0);
   outdata = cdds_malloc(nbytes);

   /* 
    * If aux opened, allocate memory for a single aux trace. 
    * (No headers used, so we know how many Samples there are)
    */
   if (aux_bin >= 0) {
      auxdata = cdds_malloc(n1_aux * sizeof(float));
   }

   /***********************************************************************
    *    open output dataset
    **********************************************************************/
   /*
      CHECK FOR ERRORS BEFORE PROCEEDING ANY FURTHER 
      'cdds_errors' function:
           This function returns the number of errors reported using
           "cdds_prterr".  No reason to create a new output if there
           have been any errors.
    */
   if (cdds_errors()) goto finish;
   /*
      CREATE OUTPUT FROM INPUT DICTIONARY 
      'cddx_outhdr' function:
           This function will create an output dataset from the input
           binary passing all it's history information and trace headers
           along.  The specified headers can be newly created or 
           existing headers and will be in order at the beginning of 
           the trace buffer, followed by any other trace headers, and
           last are the Samples.  The user can override the output 
           format and binary data by defining "out_format=" or  
           "out_data=" since the first argument is 'out'.  If you 
           simply want to pass the headers and Samples along as the  
           same data types, use cddx_out.

           With the output convenience routines, the binaries are not 
           actually opened until the binary tag is first used for I/O. 
           This way the internal buffer definitions for the output file  
           can be redefined (eg. axis, size, delta, origin...).  So no
           need to check out_bin until after it really gets opened.
    */
   out_bin = cddx_outhdr("out", "stdout:", TITLE, in_bin, DDS_FLOAT,
                       DDS_FLOAT, "RcPtXC RcPtYC StaCor");
   /*
      MODIFY THE OUTPUT AXIS 
      'cddx_dict' function:
           This function changes the dictionary mode associated with the 
           binary tag.  If redefining the 'axis', then we need to rescan
           the dictionary to get DDS to reset the axis names internally
           and set it back for printing so we can use the ".axis(1)" 
           internal DDS function instead of having to use the actual
           axis names in the definitions.
    */
   cdds_printf("axis", "%s  h %s %s\n", axis[0], axis[1], axis[2]);
   cddx_dict(out_bin, "scan");
   cddx_dict(out_bin, "print");

   /*
    * Only need to defined the parameters that might have changed from 
    * or are not in the input dataset.
    */
   cdds_printf("size.axis(1)", "%d\n", n1_out);
   cdds_printf("delta.axis(1)", "%f\n", d1_out);
   cdds_printf("size.h", "%d\n", nh);
   cdds_printf("delta.h", "%f\n", dh);
   cdds_printf("origin.h", "0\n");
   /*
      FORCE OPEN THE OUTPUT BINARIES
      'cdds_lseek' function:
          This function seeks to a specific trace position.  It is used 
          here simply to force open the internal and output binaries.
          Now we can check if it was really opened.
    */
   ier = cdds_lseek(out_bin, 0, 0, SEEK_SET);
   if (ier < 0) {
      cdds_prterr("Unable to open output dataset!\n");
   }

   /*
    * Now that the output is open, get the index to the output Samples
    */
   outsmpl = cddx_index(out_bin, "Samples", DDS_FLOAT);

   /***********************************************************************
    *    process the data
    **********************************************************************/

   if (cdds_errors()) goto finish;

   doit(in_bin, aux_bin, out_bin, ibeta, n1_out, nh, n1, n2, n3, n4,  
          n1_vel, n2_vel, n3_vel, insmpl, outsmpl, int nwrds, check, scale, 
          d1_out, dh, d1, d2, d3, d1_vel, d2_vel, d3_vel, o1, o2, o3, 
          o1_vel, o2_vel, o3_vel, indata, auxdata, vel, outdata, ctype);

   /***********************************************************************
    *    close files, clean-up, & exit
    **********************************************************************/
   /*
      CLOSE OUT
      'cdds_close' function:
           This function closes a dataset if the binary tag is >= 0
           including all underlying dictionaries and data structures.
           This will also flush out all DDS data buffers out to the
           kernel.

      'cdds_closepr' function:
           This function will close out the print file (if opened)
           adding diagnosti  information, unread command line 
           parameters and termination status.  It will also exit
           the program giving a usable status code.
    */
finish:
   cdds_close(in_bin);
   cdds_close(aux_bin);
   cdds_close(out_bin);

   cdds_closepr();
}

/***********************************************************************
 * 
 *		doit
 *
 **********************************************************************/
void doit(int in_bin, int aux_bin, int out_bin, int ibeta, int n1_out, 
          int nh, int n1, int n2, int n3, int n4, int n1_vel, int n2_vel,
          int n3_vel, int insmpl, int outsmpl, int nwrds, int check, 
          float scale, float d1_out, float dh, float d1, float d2, 
          float d3,float d1_vel, float d2_vel, float d3_vel, float o1, 
          float o2, float o3, float o1_vel, float o2_vel, float o3_vel, 
          float* indata, float* auxdata, float* vel, float* outdata,
          char* ctype)
{
   int ier, i1, i2, i3;
   float srcx, srcy, dead;

   /*
    * loop over records
    */
   for(i3=0;i3<n3;i3++) {
      /*
         READ DATA
         'cddx_read' function:
              This function reads a specified number of traces.  
              Check the number read in case of any problems.
       */
      ier = cddx_read(in_bin, indata, n2);
      if (ier != n2) {
         cdds_prterr("reading \"in\" input\n");
         return;
      }

      /*
       * process a record
       */
      for(i2=0;i2<n2;i2++) {
         /*
          * read an auxillary trace if available
          * otherwise use indata
          */
         if (aux_bin >= 0) {
            ier = cddx_read(aux_bin, auxdata, 1);
            if (ier != 1) {
               cdds_prterr("reading \"aux\" input\n");
               return;
            }
         } else {
            for(i1=0;i1<n1;i1++) {
               auxdata[i1]=indata[insmpl+i1+i2*nwrds];
            }
         } 

         /*
          * get needed input trace headers
          */
         srcx = indata[0+i2*nwrds];
         srcy = indata[1+i2*nwrds];
         dead = indata[2+i2*nwrds];
         /*
            MAP INPUT HEADERS TO OUTPUT HEADERS
            'cdds_map' function:
                 This function maps the input trace to the output trace.  
                 Using the open output convenience function, only maps 
                 trace headers (not "Samples").
          */
         cdds_map(out_bin, 0, outdata, in_bin, 0, &indata[i2*nwrds]);

         /*
          * process this trace
          */
         if (dead != 30000) {
            algorithm(n1, n2, n3, srcx, srcy, scale, ibeta, check, ctype,
                      &indata[insmpl+i2*nwrds], auxdata, vel, 
                      outdata[outsmpl]);
         }
         /*
            WRITE DATA
            'cddx_write' function:
                 This function writes a specified number of traces.  
                 Check the number written in case of any problems.
          */
         ier = cddx_write(out_bin, outdata, 1);
         if (ier != 1) {
            cdds_prterr("writing output\n");
            return;
         }
      }
   }

   return;
}