#include <stdio.h>
#include <string.h>
#include <math.h>          /* abs() */
/** 1999/07/04 Pierre Jaccard 
               Function stricmp is undefined if the next header file is not
               included. **/
#ifdef LACKS_STRICMP
#  include "misc.h"
#endif
#include "dbext.h"         /* CHAR, etc., *_VALUE_CODE, STRUCT_DEF_HDR_TYPE */
#include "prtarray.h"      /* print_array_*() */
#include "prtstruc.h"
#include "find_def.h"      /* find_def() */
/*****************************************************************************
*                                                                            *
*      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:  prtstruc.c

              Structure printing functions and supporting routines.

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

    The following external variable declarations are used by the printing
    routines below in order to keep track of the cursor position in the
    case of printing to stdout.  The variable screen_lines is initialized to
    zero each time a call to clear_screen is made, and is incremented
    by the print functions each time a line is printed to the screen.  The
    variable max_screen_lines is used as a cutoff point to pause screen
    output until the user presses the enter key, so screen output does not
    scroll off.  It is set to a default value appropriate for 24/25-line
    monitors.  It may be reset using the function set_max_screen_lines below.
    The two external variables are declared in prtarray.c.

*/
extern int screen_lines;
extern int max_screen_lines;

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

   FUNCTION: find_ncol

      Given a sample value and the print format string, it returns
      how many columns of such values can be printed on one screen line.

   PARAMETERS:

      format = the print format string

      sample_value = the value that will be printed in the given format

   RETURNS:

      the number of columns of such values that can be printed on
      an 80-character screen line

*/
#if PROTOTYPE_ALLOWED
int find_ncol(char *format, char *sample_value, SHORT value_type)
#else
int find_ncol(format, sample_value, value_type)
char *format;
char *sample_value;
SHORT value_type;
#endif
{
   char buff[255];

   switch(value_type)
   {
      case CHAR_VALUE_CODE:
      case BYTE_VALUE_CODE:
      case UBYTE_VALUE_CODE:   sprintf(buff, format, *sample_value);
                               break;
      case SHORT_VALUE_CODE:
      case USHORT_VALUE_CODE:  sprintf(buff, format, *((SHORT *) sample_value));
                               break;
      case LONG_VALUE_CODE:
      case ULONG_VALUE_CODE:   sprintf(buff, format, *((LONG *) sample_value));
                               break;
      case FLOAT_VALUE_CODE:
      case COMPLEX_VALUE_CODE: sprintf(buff, format, *((FLOAT *) sample_value));
                               break;
      case DOUBLE_VALUE_CODE:  sprintf(buff, format, *((DOUBLE *) sample_value));
                               break;
      case TEXT_VALUE_CODE:    sprintf(buff, format, sample_value);
                               break;
   }
   return(80 / max_val(1, (int)strlen(buff)));
}

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

   FUNCTION:  print_structure

        It prints a single instance of a structure as an array of ubytes
        if the structure definition is not provided, or member by member
        if the structure definition is provided.  In the latter case, the
        user-provided format or print function is used if provided, or the
        defaults otherwise.

    ARGUMENTS:

        fp = pointer to file to print to

        data = pointer to structure bytes

        name = pointer to structure name

        nbytes = no. of bytes in the structure

        str_def = pointer to head of linked list of structure definitions

    RETURNS:  no. of bytes printed

*/
#if PROTOTYPE_ALLOWED
unsigned int print_structure(FILE *fp, char *data, char *name, unsigned int nbytes, STRUCT_DEF_HDR_TYPE *str_def)
#else
unsigned int print_structure(fp, data, name, nbytes, str_def)
FILE *fp;
char *data, *name;
unsigned int nbytes;
STRUCT_DEF_HDR_TYPE *str_def;
#endif
{
   STRUCT_DEF_ENTRY_TYPE *str_def_ptr;
   FORMAT_SPEC *format_ptr;
   char *data_ptr;
   unsigned int i, j, nelem;

   if ((str_def == NULL) || ((str_def_ptr = (STRUCT_DEF_ENTRY_TYPE *)
      find_def(name, str_def)) == NULL)) /* NO STRUCTURE DEFINITION */
   {
      fprintf(fp, " %20s : STRUCTURE (%u BYTES)\n\n", name, nbytes);
      if (fp == stdout) check_screen_lines(2);
      if (nbytes > 0)
         return(print_array_1u(fp, (UBYTE *) data, nbytes, 6, "%12u"));
      else
         return(BADUINT);
   }
   data_ptr = data;
   nelem = str_def_ptr->hdr.nelem;

   for (i = 0; i < nelem; i++)
   {
      str_def_ptr += 1;
      if (str_def_ptr->elem.count != 0)
      {
         format_ptr = *(FORMAT_SPEC **) (str_def_ptr->elem.format_ptr);
         if (format_ptr)
         {
            if (format_ptr->print_function == NULL)
               data_ptr += print_select(fp, data_ptr, &(str_def_ptr->elem),
                  str_def_ptr->elem.count, format_ptr->fmt_string, str_def);
            else
            {
               fprintf(fp, format_ptr->fmt_string);
               for (j = 0; j < str_def_ptr->elem.count; j++)
                  data_ptr += (*(format_ptr->print_function))(fp, data_ptr);
            }
            if ((fp == stdout) && (format_ptr->pause))
            {
               getchar();
               clear_screen();
            }
         }
         else          /* use default print function (print_select) & format */
         {
            if (i == 0)
            {
               fprintf(fp, " %20s : STRUCTURE\n\n", name);
               if (fp == stdout) check_screen_lines(2);
            }
            data_ptr += print_select(fp, data_ptr, &(str_def_ptr->elem),
               str_def_ptr->elem.count, NULL, str_def);
            fprintf(fp, "\n");
            if (fp == stdout) check_screen_lines(1);
         }
      }
   }
   return(data_ptr - data);
}

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

   FUNCTION: print_array_struct

        It prints an array of structures, where each structure-element
        of the array is printed using the print_structure routine above.

   ARGUMENTS:

        fp = pointer to file to print to

        data = pointer to array of structures

        name = pointer to structure name

        n = no. of bytes in the array, if negative
            no. of elements in the array, if positive

        str_def = pointer to head of linked list of structure definitions

    RETURNS:  no. of bytes printed

*/
#if PROTOTYPE_ALLOWED
unsigned int print_array_struct(FILE *fp, char *data, char *name, int n, STRUCT_DEF_HDR_TYPE *str_def)
#else
unsigned int print_array_struct(fp, data, name, n, str_def)
FILE *fp;
char *data, *name;
int n;
STRUCT_DEF_HDR_TYPE *str_def;
#endif
{
   unsigned int i, byte_count = 0, struct_size = 0;

   if (n > 0) /* n is no. of elements in array of structures */
      for (i = 0; i < n; i++)
         byte_count += print_structure(fp, data+byte_count, name, 0, str_def);
   else /* n must be negative of no. of bytes in array of structures */
      while ((int) (abs(n) - byte_count) > struct_size)
      {
         struct_size = print_structure(fp, data+byte_count, name, abs(n), str_def);
         byte_count += struct_size;
      }
   return(byte_count);
}

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

   FUNCTION: print_select

      This function prints data to a file using either given or default
      formats.  The data can be of any type from VALUE_CODE--single
      element, array, or structure.

   PARAMETERS:

      fp = pointer to FILE to print to

      data = pointer to data to be printed

      nuv = pointer to structure with ff. members:

         1) name = variable name for data

         2) units = variable units ("none" or "" or NULL if not applicable)

         3) value_type = VALUE_CODE (0 to 11)

         The structure may therefore correspond to a DATA_LIST_ENTRY_TYPE
         or a STRUCT_DEF_ELEM_TYPE.

      n = no. of elements in data

      format = pointer to array of format structures for printing
               (NULL if using default formats)

      str_def = pointer to structure definitions in memory
                (NULL if non-structure or no structure definitions)

*/
#if PROTOTYPE_ALLOWED
unsigned int print_select(FILE *fp, 
			  char *data, 
			  STRUCT_DEF_ELEM_TYPE *nuv, 
			  unsigned int n, 
			  char *format, 
			  STRUCT_DEF_HDR_TYPE *str_def)
#else
unsigned int print_select(fp, data, nuv, n, format, str_def)
FILE *fp;
char *data, *format;
STRUCT_DEF_ELEM_TYPE *nuv;
unsigned int n;
STRUCT_DEF_HDR_TYPE *str_def;
#endif
{
   int ncol, no_format;
   unsigned int byte_count = 0;
   static struct
   {
      char format[15];
      int ncol;
   } VALUE_FORMAT[] =
   {
      "%12d"          ,  6,
      "%12u"          ,  6,
      "%c"            , 80,
      "%12hd"         ,  6,
      "%12hu"         ,  6,
      "%12ld"         ,  6,
      "%12lu"         ,  6,
      "%12.6g"        ,  6,
      "%12.6lg"       ,  6,
      "%11.6g+i%11.6g",  3,
      "%s"            ,  1
   };

   if (nuv->value_type == STRUCT_VALUE_CODE)
      return(print_array_struct(fp, data, nuv->name, n, str_def));
   else
   {
      no_format = (!format) || (!strcmp(format, ""));
      if (no_format)
      {
         fprintf(fp, " %20s : ", nuv->name);
         if (n > 1)
         {
            if ( (*(nuv->units)) && (stricmp(nuv->units, "none")) ) 
               fprintf(fp, "in %s ", nuv->units);
            else
	       fprintf(fp, "no units ");
            if ((nuv->value_type == CHAR_VALUE_CODE) ||
               (nuv->value_type == TEXT_VALUE_CODE))
               fprintf(fp, "(%u CHARACTERS)\n\n", n);
            else
               fprintf(fp, "(%u VALUES)\n\n", n);
            if (fp == stdout) check_screen_lines(2);
         }
         format = VALUE_FORMAT[nuv->value_type].format;
         ncol   = VALUE_FORMAT[nuv->value_type].ncol;
      }
      ncol = find_ncol(format, data, nuv->value_type);
      switch (nuv->value_type)
      {
         case BYTE_VALUE_CODE:
            byte_count = print_array_1s(fp, data, n, ncol, format);
            break;
         case UBYTE_VALUE_CODE:
            byte_count = print_array_1u(fp, (UBYTE *) data, n, ncol, format);
            break;
         case CHAR_VALUE_CODE:
            byte_count = print_array_1u(fp, (UBYTE *) data, n, ncol, format);
            break;
         case SHORT_VALUE_CODE:
            byte_count = print_array_2s(fp, (SHORT *) data, n, ncol, format);
            break;
         case USHORT_VALUE_CODE:
            byte_count = print_array_2u(fp, (USHORT *) data, n, ncol, format);
            break;
         case LONG_VALUE_CODE:
            byte_count = print_array_4(fp, (LONG *) data, n, ncol, format);
            break;
         case ULONG_VALUE_CODE:
            byte_count = print_array_4(fp, (LONG *) data, n, ncol, format);
            break;
         case FLOAT_VALUE_CODE:
            byte_count = print_array_float(fp, (FLOAT *) data, n, ncol, format);
            break;
         case DOUBLE_VALUE_CODE:
            byte_count = print_array_double(fp, (DOUBLE *) data, n, ncol, format);
            break;
         case COMPLEX_VALUE_CODE:
            byte_count = 2 * print_array_float(fp, (FLOAT *) data, n, ncol, format);
            break;
         case TEXT_VALUE_CODE:
            *(data + n - 1) = '\0';  /* make sure string is null-terminated */
            fprintf(fp, format, data);
            byte_count = n;
      }
      if ( (n == 1) && (nuv->units != NULL) && (no_format) )
      {
         if ( (*(nuv->units)) ) /* && (stricmp(nuv->units, "none")) ) */
            fprintf(fp, " %s ", nuv->units);
         else
	    fprintf(fp, " none ");
      }
   }
   return(byte_count);
}

