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

                           TEMPLATE 1

     "C" template demonstrating the use of the base dds routines
     for a simple trace-to-trace processing scheme.

     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_template1.html 1 2009-01-06 00:31:13Z ehlersjw $"
#define TITLE "template1: C template using DDS base routines"

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

/***********************************************************************
 *
 * main
 *
 **********************************************************************/
int main(int argc, char **argv)
{
   BIN_TAG in_bin=-1, in_buf_bin=-1, out_buf_bin=-1, out_bin=-1;
   const char *in_dict, *in_buf_dict, *out_buf_dict, *out_dict;
   int ier, ns, nout, tag, ndxsmp, nbytes;
   float scale, *inbuf, *outbuf;
   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_template1.html 1 2009-01-06 00:31:13Z 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 DICTIONARY 
      'cdds_in' function:
           This function opens an input dictionary.
    */
   in_dict = cdds_in("in", "stdin:", TITLE);
   /*
      OPEN THE INPUT BINARY 
      'cdds_open' function:
           This function opens the input binary.
    */
   in_bin = cdds_open(in_dict, "in_format", "in_data", "r");
   /*
      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_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 input data parameters
    */
   cdds_scanf("size.axis(1)", "%d", &ns);
      
   /***********************************************************************
    *    open input data buffer
    **********************************************************************/
   /*
    *      Define the input buffer Samples as host-dependent float
    */
   /*
      SETUP INTERNAL BUFFER REDEFINING Samples
      'cdds_dict' function:
           Setup to print definitions to the internal "override:"
           dictionary.

      'cdds_printf' function:
           Define SAMPLE_TYPE as host-dependent floats
    */
   cdds_dict("override:", "print");
   cdds_printf("fmt:*:ibuf.SAMPLE_TYPE", "\n");
   cdds_printf(" ", "   typedef float SAMPLE_TYPE;\n");    
   /*
      OPEN THE INTERNAL BUFFER
      'cdds_out' function:
           Opens the internal buffer for writing into.  

      Add aliased old_format to point to the input format so that 
      it will be used as the default output format.

      Setup internal buffer format to be "asp" via "ibuf_fmt" alias

      Open the binary for the internal buffer via "cdds_open"

    */
   in_buf_dict = cdds_out("ibuf", " ", in_dict);
   cdds_printf("$old_format", "format\n");
   cdds_printf("ibuf_fmt", "asp\n");
   in_buf_bin = cdds_open(in_buf_dict, "ibuf_fmt", " ", "m");
   if (in_buf_bin < 0) {
      cdds_prterr("Unable to open internal input buffer!\n");
   }
   /*
      INITIALIZE MAPPING FROM INPUT TO INTERNAL BUFFER
      'cdds_openm' function:
           Opens the mapping function from in_bin to in_buf_bin.
           This is NOT necessary -- DDS will make this call for you the
           first time you try and map data from in_bin to in_buf_bin. 
           However, the dictionaries must still be opened at the time.
           It is best, in general, to keep the dictionaries opened
           anyway and let the cdds_close close them at the time
           of the binary closing.
    */
   cdds_openm(in_buf_bin, 0, in_bin, 0);

   /***********************************************************************
    *    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 scale parameter (default 1.0)
    */
   scale = 1.0;
   cdds_scanf("scale", "%f", &scale);

   /*
    * get # samples out (default ns)
    */
   /*
      Use "%d" instead of "%i" because "%i" will read any values
      starting with "0" as octal numbers instead of decimal numbers.
    */
   nout = ns;
   cdds_scanf("nout", "%d", &nout);

   /***********************************************************************
    *    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("\tscale = %g\n", scale);
   cdds_prtmsg("\tnout  = %d\n", nout);
   cdds_prtmsg("\n");

   /*
    * Check parameters
    */
   if (scale < 0.0) {
      cdds_prtcon("WARNING: \"scale\" is negative!\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.

      'cdds_member' function:
           This function gets the field tag for any trace header in a
           given opened binary; the 2nd parameter, "0", specifies
           the trace sequence; the 3rd parameter specifies the name
           of the trace header -- in this case it's for "Samples".

      'cdds_index' function:
           This function gets the offset index to a trace field.
   */
   /*
    * Allocate memory for an entire input record & single output trace 
    */
   nbytes = cdds_prec(in_buf_bin, 0);
   inbuf = cdds_malloc(nbytes);

   nbytes = cdds_prec(out_buf_bin, 0);
   outbuf = cdds_malloc(nbytes);

   /*
    * get index to samples (allowing for possible trace headers)
    * (since we are not modifying any trace headers in this program,
    *  we can use the same index for both input and output buffers.)
    */
   tag = cdds_member(in_buf_bin, 0, "Samples");
   ndxsmp = cdds_index(in_buf_bin, tag, DDS_FLOAT);

   /***********************************************************************
    *    open internal output buffer
    **********************************************************************/
   /*
      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 INTERNAL BUFFER FROM INPUT BUFFER 
      Modify the output Samples type (if it is being changed by this 
      program) by printing a format change for SAMPLE_TYPE for the 
      output buffer via "obuf" alias into the "override" dictionary.

      'cdds_out' function:
           This function will create an output dictionary from a 
           previous one, passing all it's history information along.  
    */
   cdds_dict("override:", "print");
   cdds_printf("fmt:*:obuf.SAMPLE_TYPE", "\n");
   cdds_printf(" ", "   typedef complex SAMPLE_TYPE;\n");

   /*
    * open output buffer dictionary
    */
   out_buf_dict = cdds_out("obuf", " ", in_buf_dict);

   /*
    * print any definitions that might have changed from the input
    */
   cdds_printf("size.axis(1)", "%d\n", nout);

   /*      
    * open output buffer binary (using "asp" format)
    */ 
   cdds_printf("obuf_fmt", "asp\n");
   out_buf_bin = cdds_open(out_buf_dict, "obuf_fmt", " ", "m");
   if (out_buf_bin < 0) {
      cdds_prterr("Unable to open output buffer!\n");
   }

   /***********************************************************************
    *    open output data
    **********************************************************************/
   /*   
    * open the output dictionary from the internal output buffer
    */
   out_dict = cdds_out("out", "stdout:", out_buf_dict);

   /*
    * open the output binary
    */
   out_bin = cdds_open(out_dict, "out_format", "out_data", "w+");
   if (out_bin < 0) {
      cdds_prterr("Unable to open output file!\n");
   }

   /*
    * explicitly open the mapping from the internal output buffer to
    * the output dataset
    */
   cdds_openm(out_bin, 0, out_buf_bin, 0);

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

   if (cdds_errors()) goto finish;

   doit(in_bin, in_buf_bin, out_buf_bin, out_bin, ns, nout, ndxsmp, scale,
     inbuf, outbuf);

   /***********************************************************************
    *    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(in_buf_bin);
   cdds_close(out_buf_bin);
   cdds_close(out_bin);

   cdds_closepr();
}

/***********************************************************************
 * 
 *		doit
 *
 **********************************************************************/
void doit(int in_bin, int in_buf_bin, int out_buf_bin, int out_bin, 
          int ns, int nout, int ndxsmp, float scale, float* inbuf, 
          float* outbuf)
{
   int ier, i;

   /*
    * loop over each trace
    */
   /*
         READ DATA
         'cdds_readm' function:
              This function reads a specified number of traces mapping
              each into the input internal buffer.  
              Check the number read in case of any problems.
    */
   ier = cdds_readm(in_bin, 0, in_buf_bin, 0, inbuf, 1);
   while(ier == 1) {
      /*
       * save trace headers to output buffer
       */
      for(i=0;i<ndxsmp-1;i++) outbuf[i] = inbuf[i];

      /*
       * process the trace Samples
       */
      process(ns, nout, scale, &inbuf[ndxsmp], &outbuf[ndxsmp]);
  
      /*
       * write the trace
       */
      /*
            WRITE DATA
            'cdds_writem' function:
                 This function maps a specified number of traces from the
                 out_buf_bin to the out_bin and writes them out.  
                 Check the number written in case of any problems.
       */
      ier = cdds_writem(out_bin, 0, out_buf_bin, 0, outbuf, 1);
      if (ier != 1) {
         cdds_prterr("writing output\n");
         return;
      }

      /*
       *  read the next trace
       */
      ier = cdds_readm(in_bin, 0, in_buf_bin, 0, inbuf, 1);
   }
   
   return;
}
      
/***********************************************************************
 * 
 *		help
 *
 **********************************************************************/
void help()
{
   fprintf(stderr, "Template program demonstrating the use of the base\n");
   fprintf(stderr, "DDS routines for a simple trace-to-trace processing\n");
   fprintf(stderr, "scheme.\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "usage:\n");
   fprintf(stderr, "   f_template [in=dat] [in_data=bin] [in_format=fmt] \\\n");
   fprintf(stderr, "   [out=dat] [out_data=bin] [out_format=fmt] \\\n");
   fprintf(stderr, "   [nout=n] [scale=f]\n");
   fprintf(stderr, "   \n");
   fprintf(stderr, "where:\n");
   fprintf(stderr, "   in=        input dataset\n");
   fprintf(stderr, "   in_data=   input binary\n");
   fprintf(stderr, "   in_format= input format\n");
   fprintf(stderr, "   out=       output dictionary\n");
   fprintf(stderr, "   out_data=  output binary\n");
   fprintf(stderr, "   out_format=output format\n");
   fprintf(stderr, "   nout=      output samples (dflt=input)\n");
   fprintf(stderr, "   scale=     scale factor (dflt=1.0)\n");
   fprintf(stderr, "\n");

   exit(0);
}