static char rcsver[] = "$Id: output.c,v 2.2 1999/07/12 23:14:08 gorelick Exp $";
 
/**
 ** $Source: /tes/src/vanilla/RCS/output.c,v $
 **
 ** $Log: output.c,v $
 ** Revision 2.2  1999/07/12 23:14:08  gorelick
 ** *** empty log message ***
 **
 ** Revision 2.1  1999/02/10 04:00:50  gorelick
 ** *** empty log message ***
 **
 ** Revision 2.0  1998/12/22 22:47:04  gorelick
 ** release version
 **
 ** Revision 2.0  1998/12/18 01:26:03  gorelick
 ** release version
 **
 ** Revision 1.3  1998/11/12 22:58:55  gorelick
 ** first release version
 **
 **/

#include <stdio.h>
#include <assert.h>
#include <math.h>
#include "header.h"
#include "proto.h"


extern DATA  ConvertVarData(PTR raw, VARDATA *vdata);
PTR   GiveMeVarPtr(PTR raw, TABLE *table, int offset);
/* extern int   ConvertVaxVarByteCount(PTR raw, VARDATA *vdata / * ??? * /); */

static short VARTEST[] = {12,10,11,12,13,14,15,12};

/* #define GiveMeVarPtr(raw, table, offset)    ((void *)VARTEST) */

typedef void (*PrintFunc)(void *, OSTRUCT *);
typedef void (*VarPrintFunc)(void *, VARDATA *);



/** INTERNAL INTERFACES **/

/*
** print_X() prints a value of type X by taking a pointer to the
** raw value of the field and an OSTRUCT.
**
** print_scaled_X() prints the scaled value of type X by applying
** the specified scale and offset values.
**
** An OSTRUCT is being passed instead of a FIELD structure to keep
** the parameters of all the major print functions the same. This
** allows us to cache a print function pointer and call it for each
** output operation of that field.
*/

static void print_int(PTR raw, OSTRUCT *oinfo);
static void print_uint(PTR raw, OSTRUCT *oinfo);
static void print_real(PTR raw, OSTRUCT *oinfo);
static void print_string(PTR raw, OSTRUCT *oinfo);

static void print_scaled_int(PTR raw, OSTRUCT *oinfo);
static void print_scaled_uint(PTR raw, OSTRUCT *oinfo);
static void print_scaled_real(PTR raw, OSTRUCT *oinfo);


/*
** print_array() prints the array of elements stored starting at
** the memory location stored in raw.
*/

static void print_array(PTR raw, OSTRUCT *oinfo);


/*
** print_X_var_data() prints either the pointer to the variable
** data or the variable data items itself. Variable data record
** type "X" can be either VAX or Q15. The raw pointer points
** towards the file-pointer of the variable data within the variable
** part of the table.
*/

static void print_vax_var_data(PTR raw, OSTRUCT *oinfo);
static void print_q15_var_data(PTR raw, OSTRUCT *oinfo);



/*
** var_print_X() set of functions is the counterpart of the
** print_X() functions above, except that these work on VARDATA
** structures and pointers to a value of type X. These have been
** created to speedup the process by avoiding unnecessary conditions.
*/

static void var_print_int(PTR raw, VARDATA *vardata);
static void var_print_uint(PTR raw, VARDATA *vardata);
static void var_print_real(PTR raw, VARDATA *vardata);
static void var_print_string(PTR raw, VARDATA *vardata);


/*
** select_atom_output_func() selects a function for the output of
** the specified data type. It checks to see if a scaled or an
** unscaled function should be used.
*/
static FuncPtr select_atom_output_func(IFORMAT format, int is_scaled);

/*
** select_var_atom_output_func() is the counterpart of
** select_atom_output_func() except that it works on atoms that can
** appear within the variable data
*/
static FuncPtr select_var_atom_output_func(IFORMAT format, int is_scaled);


/** IMPLEMENTATION **/


static void
print_scaled_int(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);

  printf("%.11g", ((double)data.i) * field->scale + field->offset);
}

static void
print_scaled_uint(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);

  printf("%.11g", ((double)data.ui) * field->scale + field->offset);
}

static void
print_scaled_real(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);

  printf("%.11g", data.r * field->scale + field->offset);
}



static void
print_int(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);

  printf("%i", data.i);
}

static void
print_uint(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);

  printf("%u", data.ui);
}

static void
print_real(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);
  
  printf("%.11g", data.r);
}

static void
print_string(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA data = ConvertData(raw, field);
  
  printf("%*.*s", field->size, field->size, data.str);
}



static void
print_array(PTR raw, OSTRUCT *oinfo)
{
  int i;
  FIELD *field = oinfo->field;
  PrintFunc p = (PrintFunc)oinfo->cooked.print_func2;

  for (i = oinfo->cooked.range.start; i <= oinfo->cooked.range.end; i++){
    (*p)(raw+(i-1)*field->size, oinfo);
    if (i < oinfo->cooked.range.end){ printf("%c", O_DELIM); }
  }
}



static void
var_print_int(PTR raw, VARDATA *vardata)
{
  DATA data = ConvertVarData(raw, vardata);

  printf("%i", data.i);
}


static void
var_print_uint(PTR raw, VARDATA *vardata)
{
  DATA data = ConvertVarData(raw, vardata);

  printf("%u", data.ui);
}


static void
var_print_real(PTR raw, VARDATA *vardata)
{
  DATA data = ConvertVarData(raw, vardata);

  printf("%.11g", data.r);
}


static void
var_print_string(PTR raw, VARDATA *vardata)
{
  DATA data = ConvertVarData(raw, vardata);

  printf("%*.*s", vardata->size, vardata->size, data.str);
}




static void
print_q15_var_data(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA   data = ConvertData(raw, field);
  int    m, n;
  double scale;
  int    i;

  /* Assumption: VarPtr is always a signed-integer */
  if(oinfo->cooked.is_atomic){
    printf("%#x", data.i);
  }
  else {
    if (data.i < 0){ /* No variable data */
      raw = NULL;
      n = 0;
      scale = 0;
    }
    else { /* Have some variable data */
      raw = GiveMeVarPtr(raw, oinfo->table, data.i);
      assert(raw != NULL); /* Fatal memory reference error */
      
      /* Assumption: byte size only contains the size of data bytes
         excluding the header and trailer byte size */
      /* Read (signed int) byte size of variable data items from
         the head 2-bytes */
      n = ConvertVaxVarByteCount(raw, field->vardata)
        / field->vardata->size;
      raw += 2; /* Skip the byte size */

      assert(n > 0); /* Exponent must be there, otherwise, Q15 is incomplete */

      /* Assumption: Q15 exponent is always a signed integer */
      /* Read the Q15 (signed int) exponent (value) from the head 2-bytes */
      scale = pow(2, (double)ConvertVarData(raw, oinfo->field->vardata).i-15);
      raw += 2; /* Skip the exponent */

      /* Subtract 1 to exclude the quotient from the count of elements */
      n--;
    }
    
    m = oinfo->cooked.range.end;
    /* m < 0: Output all the elements there are. If there are none, output
              atleast one "n/a" and hence if (n == 0){ n = 1; }
       m = 0: (Already handled somewhere up) Output the pointer values */
    if (m < 0){ m = n; if (n == 0){ n = 1; } } 
    else if (m > n){ int t = m; m = n; n = t; } /* Make sure m <= n */
    else if (m < n){ n = m; }

    /* Number of avaialable data items to output */
    for (i = oinfo->cooked.range.start; i <= m; i++){
      printf("%.11g", (double)ConvertVarData(raw+(i-1)*field->vardata->size,
                                           field->vardata).i * scale);
      if (i < n){ printf("%c",O_DELIM); }
    }
    
    for (; i <= n; i++){ /* Number of n/a's to output */
      printf("N/A");
      if (i < n){ printf("%c",O_DELIM); }
    }
  }
}


static void
print_vax_var_data(PTR raw, OSTRUCT *oinfo)
{
  FIELD *field = oinfo->field;
  DATA   data = ConvertData(raw, field);
  int    m, n;
  double scale;
  VarPrintFunc p;
  int    i;

  /* Assumption: VarPtr is always a signed-integer */
  if(oinfo->cooked.is_atomic){
    printf("%#x", data.i);
  }
  else {
    if (data.i < 0){ /* No variable data */
      raw = NULL;
      n = 0;
    }
    else { /* Have some variable data */
      raw = GiveMeVarPtr(raw, oinfo->table, data.i);
      assert(raw != NULL); /* Fatal memory reference error */
      
      /* Assumption: byte size only contains the size of data bytes
         excluding the header and trailer byte size */
      /* Read (signed int) byte size of variable data items from
         the head 2-bytes */
      n = ConvertVaxVarByteCount(raw, field->vardata)
        / field->vardata->size;
      raw += 2; /* Skip the byte size */
    }
    
    m = oinfo->cooked.range.end;
    /* m < 0: Output all the elements there are. If there are none, output
              atleast one "n/a" and hence if (n == 0){ n = 1; }
       m = 0: (Already handled somewhere up) Output the pointer values */
    if (m < 0){ m = n; if (n == 0){ n = 1; } } 
    else if (m > n){ int t = m; m = n; n = t; } /* Make sure m <= n */
    else if (m < n){ n = m; }

    /*
    switch(field->vardata->iformat){
    case INT:
      p = var_print_int;
      break;

    case UINT:
      p = var_print_uint;
      break;

    case REAL:
      p = var_print_real;
      break;

    case STRING:
      p = var_print_string;
      break;

    default:
      fprintf(stderr, "Vax Variable Data: Unknown format! Aborting.\n");
      abort();
      break;
    }
    */
    p = (VarPrintFunc)oinfo->cooked.print_func2;

    /* Output available data items */
    for (i = oinfo->cooked.range.start; i <= m; i++){
      (*p)(raw+(i-1)*field->vardata->size, field->vardata);
      if (i < n){ printf("%c",O_DELIM); }
    }

    /* Output rest of elements as N/As */
    for (; i <= n; i++){
      printf("N/A");
      if (i < n){ printf("%c",O_DELIM); }
    }
  }
}


static FuncPtr
select_atom_output_func(IFORMAT format, int is_scaled)
{
  FuncPtr p;

  switch(format){
  case INT:
    if (is_scaled){ p = (FuncPtr)print_scaled_int; }
    else { p = (FuncPtr)print_int; }
    break;

  case UINT:
    if (is_scaled){ p = (FuncPtr)print_scaled_uint; }
    else { p = (FuncPtr)print_uint; }
    break;

  case REAL:
    if (is_scaled){ p = (FuncPtr)print_scaled_real; }
    else { p = (FuncPtr)print_real; }
    break;

  case STRING:
    p = (FuncPtr)print_string;
    break;

  default:
    p = NULL;
    break;
  }

  return p;
}


static FuncPtr
select_var_atom_output_func(IFORMAT format, int is_scaled)
{
  FuncPtr p;
  
  if (is_scaled){
    fprintf(stderr, "Fatal! Variable data type not suppored.\n");
    abort();
  }

  switch(format){
  case INT:    p = (FuncPtr)var_print_int;    break;
  case UINT:   p = (FuncPtr)var_print_uint;   break;
  case REAL:   p = (FuncPtr)var_print_real;   break;
  case STRING: p = (FuncPtr)var_print_string; break;
  default:     p = (FuncPtr)NULL;             break;
  }

  return p;
}



void
setup_output_parameters(OSTRUCT **oinfo, int n, int defeat_scale)
{
  int i;
  FuncPtr p;

  for (i = 0; i < n; i++){
    if (oinfo[i]->field->dimension > 0){ /* An array field */
      /* Cache the array output range, and print funcions */
      if (oinfo[i]->range.end <= 0){
        oinfo[i]->cooked.range.end = oinfo[i]->field->dimension;
      }
      else {
        oinfo[i]->cooked.range.end = MIN(oinfo[i]->range.end,
                                         oinfo[i]->field->dimension);
      }
      oinfo[i]->cooked.range.start = MAX(oinfo[i]->range.start, 1);
      oinfo[i]->cooked.is_atomic = 0;
      oinfo[i]->cooked.print_func = (FuncPtr)print_array;

      /* Select the function to do output of individual elements */
      p = select_atom_output_func(oinfo[i]->field->iformat,
                                  !defeat_scale && oinfo[i]->field->scale);
      oinfo[i]->cooked.print_func2 = p;

      if (p == NULL){
        fprintf(stderr, "Fatal! Format unsupported.\n");
        abort();
      }
    }
    else {
      if (oinfo[i]->field->vardata == NULL){ /* Atomic field */
        oinfo[i]->cooked.is_atomic = 1;

        /* Select the function to do the field output */
        p = select_atom_output_func(oinfo[i]->field->iformat,
                                    !defeat_scale && oinfo[i]->field->scale);
        oinfo[i]->cooked.print_func = p;
        /* Set output extension function to NULL, since we are
           doing an atomic-field output. */
        oinfo[i]->cooked.print_func2 = NULL;

        if (p == NULL){
          fprintf(stderr, "Fatal! Format unsupported.\n");
          abort();
        }
      }
      else { /* Variable data field */
        /* Select the output routine to use */
        switch(oinfo[i]->field->vardata->type){
        case Q15:
          oinfo[i]->cooked.print_func = (FuncPtr)print_q15_var_data;
          break;
          
        case VAX_VAR:
          oinfo[i]->cooked.print_func = (FuncPtr)print_vax_var_data;
          oinfo[i]->cooked.print_func2 =
            select_var_atom_output_func(oinfo[i]->field->vardata->iformat,0);
          break;
          
        default:
          fprintf(stderr, "Fatal! Variable data type unsupported.\n");
          abort();
          break;
        }
        
        if ((oinfo[i]->range.start == 0) && (oinfo[i]->range.end == 0)){
          /* Atomic field output as a hex file pointer */
          oinfo[i]->cooked.is_atomic = 1;
        }
        else {
          /* Array field output upto either the specified limit or
             uptil the end of available data (specified by a -ve
             number as the end value) */
          oinfo[i]->cooked.is_atomic = 0;
          oinfo[i]->cooked.range.start = MAX(oinfo[i]->range.start, 1);
          if (oinfo[i]->range.end == 0){
            /* Shouldn't happen though */
            oinfo[i]->cooked.range.end = -1;
          }
          else {
            oinfo[i]->cooked.range.end = oinfo[i]->range.end;
          }
        }
      }
    }
  }
}


void
output_rec(OSTRUCT *ofieldsx[], int n)
{
  int i;
  PTR raw;

  for (i = 0; i < n; i++){
    raw = ofieldsx[i]->slice->start_rec + ofieldsx[i]->field->start;
    (*(PrintFunc)ofieldsx[i]->cooked.print_func)(raw, ofieldsx[i]);
    if (i < n-1){ printf("%c", O_DELIM); }
  }
  printf("\n");
}