/*****************************************************************************
*                                                                            *
*      COMMON OCEANOGRAPHIC DATA ACCESS SYSTEM (CODAS)                       *
*                                                                            *
*      WRITTEN BY:  RAMON CABRERA, ERIC FIRING, and JULIE RANADA             *
*                   JOINT INSTITUTE FOR MARINE AND ATMOSPHERIC RESEARCH      *
*                   1000 POPE ROAD  MSB 404                                  *
*                   HONOLULU, HI 96822                                       *
*                                                                            *
*      VERSION:     3.00                                                     *
*                                                                            *
*      DATE:        APRIL 1989                                               *
*                                                                            *
*****************************************************************************/
/*

       FILE:  dblscale.c

              based on scale.c 2000/09/17 EF
              This could be enormously simplified, including
              elimination of the horrendous function array,
              by exclusive use of pointers plus value types.
              The double type is the intermediary in any case,
              so we could convert from anything to anything
              quite generally with a single function.

              Improvement over scale.c: by switching the first
              input argument to a void pointer, I eliminated
              the "initialization from incompatible..."
              warnings.

              EF 2000/09/19


*/
/*-----------------------------------------------------------------------------

  FUNCTIONS:  SCALE_FLOAT_TO_??

      The scaling functions accept an array of floating point numbers and
      perform the inverse of the unscaling operations. The results are
      returned in an array of the specified data type.

      Note that the scaling and unscaling functions are defined to be
      inverse operations when called with the same arguments for offset
      and scale.

      Results: ??_array[i] = (float_array[i] - *offset) / *scale
               float_array[i] = *offset + *scale * ??_array[i]

      For example, for soundspeed the offset is 1500 m/s and
      the new scale is 1E-2 m/s.

      These functions can be used on single values by calling them
      with the addresses of the variables and setting npts to 1.

   PARAMETERS:

      ??_array = the array of data type specified by ??; for the scaling
                 functions, this array is used to store the result of the
                 scaling operation;

      float_array = the array of floating point numbers; for scaling, this
                    contains the values that must be scaled and converted to
                    some other data type;

      scale = pointer to the number by which a value must be factored in order
              to convert from the units in which it is expressed to some
              other unit.

      offset = pointer to the number by which a value must be reduced/increased
               in order to convert from the units in which it is expressed
               to some other unit.

      nv   = pointer to the number of values to be scaled

      nbad = on exit, this contains the number of values in the
             resulting array that have been set to BAD?? because
             the scaling result was out of range.

*/
#define out_of_range(min, max) (fs < min || fs > max ? printf("\n WARNING: attempt to convert float out of range\n original float: %f\n scaled: %f", float_array[i], fs) : 0 )
/*---------------------------------------------------------------------------*/

#include <stdio.h>
#include "dbext.h"  /* FLOAT, etc., MIN*, MAX*, BAD*, ZERO_SCALE */
#include "find_def.h"
#include "ioserv.h"
#include "dblscale.h"
#include "dbglo.h"     /* VALUE_SIZE */

extern NAME_LIST_ENTRY_TYPE VALUE_CODE[];

int SCALE_DOUBLE_TO_BYTE(void *byte_array_,
                         DOUBLE float_array[],
                         FLOAT *scale, FLOAT *offset,
                         unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   BYTE *byte_array = byte_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      byte_array[i] = BADBYTE;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range(MINBYTE, MAXBYTE)) byte_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_UBYTE(void *ubyte_array_,
                          DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                          unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   UBYTE *ubyte_array = ubyte_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      ubyte_array[i] = BADUBYTE;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range((double)MINUBYTE, (double)MAXUBYTE)) ubyte_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_SHORT(void *short_array_,
                          DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                          unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   SHORT *short_array = short_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      short_array[i] = BADSHORT;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (long int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range(MINSHORT, MAXSHORT)) short_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_USHORT (void *ushort_array_,
                            DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                            unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   USHORT *ushort_array = ushort_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      ushort_array[i] = BADUSHORT;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (unsigned long int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range((double)MINUSHORT, (double)MAXUSHORT)) ushort_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_LONG(void *long_array_,
                         DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                         unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   LONG *long_array = long_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      long_array[i] = BADLONG;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = round( (float_array[i] - *offset) / *scale );
         if (!out_of_range((double)MINLONG, (double)MAXLONG)) long_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_ULONG(void *ulong_array_,
                          DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                          unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   ULONG *ulong_array = ulong_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      ulong_array[i] = BADULONG;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = round( (float_array[i] - *offset) / *scale );
         if (!out_of_range(MINULONG, MAXULONG)) ulong_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_FLOAT(void *float2_array_,
                          DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                          unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   FLOAT *float2_array = float2_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      float2_array[i] = BADFLOAT;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (float_array[i] - *offset) / *scale;
         if (!out_of_range(-MAXFLOAT, MAXFLOAT)) float2_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
int SCALE_DOUBLE_TO_DOUBLE(void *double_array_,
                           DOUBLE float_array[], FLOAT *scale, FLOAT *offset,
                           unsigned int *nv, unsigned int *nbad)
{
   unsigned int i;
   double fs;
   DOUBLE *double_array = double_array_;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      double_array[i] = BADDOUBLE;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (float_array[i] - *offset) / *scale;
         if (!out_of_range(-MAXDOUBLE, MAXDOUBLE)) double_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}

int (*dblscale[])(void *out_array, DOUBLE in_array[], FLOAT *scale,
   FLOAT *offset, unsigned int *nv, unsigned int *nbad) =
{
   SCALE_DOUBLE_TO_BYTE,
   SCALE_DOUBLE_TO_UBYTE,
   SCALE_DOUBLE_TO_UBYTE,  /* CHAR is UBYTE */
   SCALE_DOUBLE_TO_SHORT,
   SCALE_DOUBLE_TO_USHORT,
   SCALE_DOUBLE_TO_LONG,
   SCALE_DOUBLE_TO_ULONG,
   SCALE_DOUBLE_TO_FLOAT,
   SCALE_DOUBLE_TO_DOUBLE
};

/*-----------------------------------------------------------------------------

   FUNCTION:  dbl_struc


    ARGUMENTS:

        array = pointer to array of doubles

        structure = pointer to structure bytes

        name = pointer to structure name

        str_def = pointer to head of linked list of structure definitions

    RETURNS:  0 on error, size of structure in bytes on success

*/
int dbl_struc(double *array, char *structure,
                              char *name,
                              STRUCT_DEF_HDR_TYPE *str_def)
{
   STRUCT_DEF_ENTRY_TYPE *str_def_ptr;
   char *data_ptr;
   unsigned int i, nelem, nc, ierr, nbad;
   float float_zero = 0.0,
         float_one  = 1.0;

   if ((str_def == NULL) || ((str_def_ptr = (STRUCT_DEF_ENTRY_TYPE *)
      find_def(name, str_def)) == NULL)) /* NO STRUCTURE DEFINITION */
   {
      fprintf(stderr, " Definition missing for %20s\n\n", name);
      return 0; /* error; fix error reporting later */
   }
   data_ptr = structure;
   nelem = str_def_ptr->hdr.nelem;

   for (i = 0; i < nelem; i++)
   {
      str_def_ptr += 1;
      nc = str_def_ptr->elem.count;
      if (nc > 0)
      {
         if (str_def_ptr->elem.value_type == STRUCT_VALUE_CODE)
         {
            fprintf(stderr, "Recursion not yet implemented");
            return 0;
         }
         else if (str_def_ptr->elem.value_type > DOUBLE_VALUE_CODE)
         {
            fprintf(stderr, "Type %d not supported.\n",
                        str_def_ptr->elem.value_type);
            return 0;
         }
         else
         {
            ierr = (*dblscale[str_def_ptr->elem.value_type])(
                      data_ptr, array,
                      &float_one, &float_zero, &nc, &nbad);
            data_ptr += nc * VALUE_SIZE[str_def_ptr->elem.value_type];
            array += nc;
         }
      }
   }
   return (data_ptr - structure);
}

#if 0
/* Test and example. */
int main(int argc, char **argv)
{
   double array[20];

   STRUCT_DEF_HDR_TYPE *struct_def;
   FORMAT_HDR_TYPE *formats;
   USHORT nentries;
   struct {SHORT e1, e2; CHAR e3[4];} test1a, test1 = { 1, 2, "abc"};
   struct {DOUBLE e1; FLOAT e2[2];} test2a, test2 = { 5.43, {2.1, 1.2}};
   int ierr, nbytes;


   ierr = load_structure_definition("teststruct.def", &struct_def,
                              &nentries, &formats);

   printf("ierr = %d, nentries = %d\n", ierr, nentries);

   array[0] = 1; array[1] = 2; array[2] = 'a'; array[3] = 'b'; array[4] = 'c';
   array[5] = 0;
   nbytes = dbl_struc(array, (char *)&test1a, "TEST1", struct_def);
   printf("nbytes = %d\n", nbytes);
   printf("\n%f %f  %f %f %f  %f\n", array[0], array[1], array[2],
           array[3], array[4], array[5]);
   printf("%d %d %s\n", test1a.e1, test1a.e2, test1a.e3);

   return 0;
}

#endif


