/* @(#)printsf.c	1.9	2/10/93 */
/*******************************************************************************

This code was written and designed by 
Andrew Gene HALL of I.N. Services Pty. Ltd., Scarborough WA, Australia,
for the 
University of Western Australia, Crawley WA, Australia.

*******************************************************************************/
#include <stdio.h>
#include <memory.h>
#include "utilitys.h"
#include "diagmesg.h"
#include "printsf.h"

#define INDENT		4	/* number of spaces between items */
#define INDENT_STR	"    "	/* the string to indent by */

extern int  line_max; /* CHANGE 17/6 */
int line_len = 0;

int
Number_of_Names(
Data_Name_Seq	*d )
{
   register int	i = 0;

   for(; d; d = d->next) 
      switch( d->tag ) {

      case NAME:
         i++;
         break;

      case SUB_LOOP:
         i = i + Number_of_Names( d->node.sub_loop );
         break;
      default:
         ERROR_MSG("Number_of_Names: Unknown data in Data Name Sequence");
      }
   return( i );
} /* Number_of_Names */

/* forward declare sb string and char putters */
int sb_fputs(char *s, FILE *fp)
{
   int len;
   len = strlen(s);
   if( len + line_len > line_max)
   {
       fputc('\n', fp);
       fputc(' ', fp);
       line_len = 1;
   }
   line_len = line_len + len;
   fputs( s, fp);
}

int sb_fputc(int c, FILE *fp)
{
   if (c == '\n') line_len = 0;
   else if( ++line_len > line_max)
   {
       fputc('\n', fp);
       line_len = 1;
   }
   fputc( c, fp);
}


int
fprint_Data_Item(
register FILE *	fp,
register Data_Item	*t )
{
   register int	i = 0;

   if( t )
      switch( t->tag ) {

      case DOUBLE_QUOTED_TEXT_STRING_DI:
         i = sb_fputc( '"', fp);
         i = sb_fputs( t->value, fp);
         i = sb_fputc( '"', fp);
         break;

      case SEMI_COLON_BOUNDED_TEXT_STRING_DI:
         i = sb_fputc( '\n', fp);
         i = sb_fputc( ';', fp);
         i =    fputs( t->value, fp);
         i = sb_fputc( ';', fp);
         i = sb_fputc( '\n', fp);
         break;

      case SINGLE_QUOTED_TEXT_STRING_DI: 
         i = sb_fputc( '\'', fp);
         i = sb_fputs( t->value, fp);
         i = sb_fputc( '\'', fp);
         break;

      case NON_QUOTED_TEXT_STRING_DI:
      case FRAME_CODE_DI:
         i = sb_fputs( t->value, fp);
         break;

      default:
         ERROR_MSG("fprint_Data_Item(): Unknown Data Item to print.");
      }
   else
      ERROR_MSG("fprint_Data_Item(): No Data Item to print.");
   return(i);
} /* fprint_Data_Item */

int
fprint_Loop_Data(
register FILE		*fp,
         Data_Name_Seq	*start,
         List_Value_Seq	*data[],
register int		indents)
{
   register Data_Name_Seq	*t;
   register int			i, j, N;
            Boolean		there_is_data = (Boolean) start;

   if( ! there_is_data ) {
      ERROR_MSG("fprint_Loop_Data(): Not given a template to print data by.");
      return( i );
   }
   do {
      DEBUG_MSG("fprint_Loop_Data: Starting a fresh packet.");
      /* print a packet of data */
      /* start the next packet on a newline */
/*    i = fprintf( fp, "\n"); */
      i = sb_fputc( '\n', fp);
      for( i = indents; i; i--) sb_fputs( INDENT_STR, fp);
      j = 0;
      t = start;
      do {
         if( data[j] ) {
            switch( t->tag ) {
            case NAME:
               switch( data[j]->tag ) {

               case DATA_ITEM:
                  /* print and shift pointers along */
                  i = fprint_Data_Item(fp, data[j]->element.data_item );
                  i = sb_fputs( " ", fp);
                  data[j] = data[j]->next;
                  j = j + 1;
                  break;

               case SUB_LOOP:
               default:
                  ERROR_MSG("fprint_Loop_Data: Misalligned data, DATA_ITEM expected.");
               }
               break;

            case SUB_LOOP:
               switch( data[j]->tag ) {
               case SUB_LOOP: {
                  /* build a new array */
                  int	n, k;
                  List_Value_Seq	**tmp;
  
                  n = Number_of_Names( t->node.sub_loop );
                  tmp = (List_Value_Seq **)malloc( n * sizeof(List_Value_Seq *));
                  for( k =0; k < n; k++) {
                     tmp[k] = data[j]->element.sub_loop;
                     data[j] = data[j]->next;
                     j = j +1;
                  }
  
                  /* print and shift pointers along */
                  /* i = fprintf( fp, "\n");*/	/* start on a fresh line */
                  if( (i = fprint_Loop_Data(fp, t->node.sub_loop, tmp, 
                                            indents +1)) >= 0)
                     /* not all the data was printed */
                     return( i );
          /*      i = fprintf( fp, "    \tstop_");*/
                  i = sb_fputs( "stop_", fp);
                  i = sb_fputc( '\n', fp);
                  for( i = indents; i; i--) sb_fputs( INDENT_STR, fp);
                  free( tmp );
                  }
                  break;

               case DATA_ITEM:
               default:
                  ERROR_MSG("fprint_Loop_Data: Misalligned data, SUB_LOOP expected."); 
               }
               break;

            default:
               ERROR_MSG("fprint_Loop_Data: Unknown data in Data_Name_Sequence.");
            } /* end case switch */
            t = t->next ;	/* look at the next field in loop */
         }
         else {	/* at the end of the data */
            DEBUG_MSG("fprint_Loop_Data: No more data available.");
            there_is_data = FALSE;
            t = NULL;	/* jump to the end of processing */
            if( j )
              /* under normal circumstances, 
                 you should only run out of data at the first data */
              return( j );
         }
      } while( t );
   } while( there_is_data );

   /* check that all data was printed */
   N = Number_of_Names( start );
   i = 1;
   while( i < N )
      /* find the first data with still some left */
      if( data[i] )
         /* there is still some data left */
         /* break here, where there is extra data */
         return( i );
      else
         i++;

   /* all data was printed */
   return( -1 );
} /* fprint_Loop_Data */

int
fprint_Data_Name_Seq(
register FILE		*fp,
register Data_Name_Seq	*t,
register int		indents )
{
   register int	i;
   char 	*indent_str;

   /* build the indent string */
   if( ! (indent_str = (char *)malloc
                       ((((indents +1) * INDENT) +1) * sizeof(char) )) )
      ERROR_MSG( "fprint_Data_Name_Seq : out of memmory" );
   indent_str[0] = '\0';
   for( i = indents; i; i-- ) (void)strcat( indent_str, INDENT_STR);
   /* print loop_ */
/* i = fprintf( fp, "%sloop_\n", indent_str);*/
    i = sb_fputs( indent_str, fp);
    i = sb_fputs( "loop_"   , fp);
    i = sb_fputc( '\n'      , fp);
   /* indent again */
   (void)strcat( indent_str, INDENT_STR);

   for( ; t; t = t->next) {
      switch( t->tag ) {

      case NAME:
/*         i = fprintf( fp, "%s%s\n", indent_str, t->node.name); */
           i = sb_fputs( indent_str, fp);
           i = sb_fputs( t->node.name, fp);
           i = sb_fputc( '\n'      , fp);
         break;

      case SUB_LOOP:
         DEBUG_MSG("Recusive call to fprint_Data_Name_Seq.");
         i = fprint_Data_Name_Seq( fp, t->node.sub_loop, indents + 1 );
/*       i = fprintf( fp, "%sstop_\n", indent_str);*/
         i = sb_fputs( indent_str, fp);
         i = sb_fputs( "stop_"   , fp);
         i = sb_fputc( '\n'      , fp);
         break;

      default:
         ERROR_MSG("fprint_Data_Name_Seq: Unknown data in Data Name Sequence.");
      } /* end case switch */
   } /* loop for ( t == NULL ) */
   /* pass back error result */
   return( i );
} /* fprint_Data_Name_Seq */

/* path_thru_DNS
 * Given a Data_Name_Seq and a name to search for,
 * Return the path pointers to that Data_Name, and update the variable
 * pointing to its length.
 * Return NULL, if no path to the name can be found.
 */
Data_Name_Seq	**path_thru_DNS(
Data_Name_Seq	*original,
char		*name,
int		*length)
{
   Data_Name_Seq	**path = NULL;

   while( original ) {
      switch( original->tag ) {
      
      case NAME:
         if( original->node.name == name ) {
            Malloc( Data_Name_Seq *, path);
            path[0] = original;
            *length = 1;
            original = NULL;
         }
         else
            original = original->next;
         break;

      case SUB_LOOP:
         {
         int		n = 0;
         Data_Name_Seq	**t;

         if( (t = path_thru_DNS( original->node.sub_loop, name, &n)) ) {
            Malloc( Data_Name_Seq *, path);
            path[0] = original;
            if( (path = (Data_Name_Seq **)realloc( path,
                                          (n + 1) * sizeof(Data_Name_Seq *)))) {
               /* append the returned path on the end */
               (void)memcpy( path + 1, t, n * sizeof(Data_Name_Seq *));
               free( t );
               *length = n +1;
            }
            else {
               ERROR_MSG("path_thru_DNS(): Out of memory.");
               path = NULL;
            }
            original = NULL;
         }
         else
            original = original->next;
         }
         break;

      default:
         ERROR_MSG("path_thru_DNS(): Unknown domain in Data Name Sequence.");
      }
   }
   return( path );
} /* path_thru_DNS */

int
fprint_Data_Seq( 
register FILE		*fp,
register Data_Seq	*t )
{

register int		i;
register Data_Seq	*last;
register Boolean	in_the_same_loop;
         int		n, k;
         List_Value_Seq	**tmp;
         Boolean	add_padding = FALSE;

   /* Scan the entire sequence of data */
   while( t ) {
      /* Determine the data's type */
      switch( t->tag ) {

      case DATA_ITEM:
         if( add_padding ) sb_fputc( '\n', fp);
         /* data_name <tab> text_string <new-line> */
     /*  i = fprintf( fp, "%s    \t", t->name); */
         i = sb_fputs( t->name   , fp);
         i = sb_fputs( "    \t"   , fp);
         i = fprint_Data_Item( fp, t->data.data_item);
         i = sb_fputc( '\n', fp);
         t = t->next;
         break;

      case LOOP: {
         Data_Name_Seq	*save_last_DNS, *last_DNS, *tmp_DNS, *s;
         Data_Name_Seq	**next_path, **prev_path;
         int		j, len;
         Data_Seq	*next_data;
         Boolean	first_diff;
         FILE		*tmp_fp = NULL;
         char		tmp_fname[L_tmpnam];


         /* find the last piece of data that is in this loop */
         n = Number_of_Names( t->data.loop.packet_members );
      while( n >= 0){
         /* build array of pointers to values we wish to print */
         last = t;
         tmp = (List_Value_Seq **)malloc( n * sizeof(List_Value_Seq *));
         k = 0;
         in_the_same_loop = TRUE;
         while( in_the_same_loop && ( k < n) ) 
            if( last )
               if( last->tag == LOOP )
                  if(t->data.loop.packet_members == last->data.loop.packet_members) {
                     tmp[k++] = last->data.loop.values;
                     /* this pushes "last" beyond the last piece of data in this loop */
                     last = last->next;
                  }
                  else	/* next piece of data belongs to a different loop */
                     in_the_same_loop = FALSE;
               else	/* next piece of data is not even in a loop */
                  in_the_same_loop = FALSE;
            else	/* no more data at all */
               in_the_same_loop = FALSE;
         /* shrink the block down to only the size it needs to be */
         n = k;
         tmp = (List_Value_Seq **)realloc(tmp, n * sizeof(List_Value_Seq *));

         /* build a Data_Name_Seq to reflect tmp */
         len = 0;
         prev_path = path_thru_DNS( t->data.loop.packet_members, t->name, &len);
         tmp_DNS = NULL;
         for( k = 0; k < len; k++ ) {
             Malloc( Data_Name_Seq, s );
             if( prev_path[k]->tag == NAME ) {
                s->tag = NAME;
                s->node.name = t->name;
                s->next = NULL;
             }
             else if( prev_path[k]->tag == SUB_LOOP ) {
                s->tag = SUB_LOOP;
                s->node.sub_loop = NULL;
                s->next = NULL;
             }
             else
                ERROR_MSG("Unknown data type.");
             if( tmp_DNS ) {
                /* assume that the last one is a sub loop */
                last_DNS->node.sub_loop = s;
                last_DNS = s;
             }
             else
                tmp_DNS = last_DNS = s;
          }
         /* now add the others */
         save_last_DNS = last_DNS = tmp_DNS;
         next_data = t->next;
         for( j =1; j < n; j++ ) {
            len = 0;
            next_path = path_thru_DNS( t->data.loop.packet_members, next_data->name, &len);
/*
for(k=0;k<len;k++) printf("next_path[%d] = %p\n",k,next_path[k]);
*/
            /* determine where the paths differ, then build from there */
            first_diff = TRUE;
            for( k = 0; k < len; k++ ) {
                if( next_path[k] == prev_path[k] ) {
                   /* assume there is something down there to descend to */
                   last_DNS = last_DNS->node.sub_loop;
                   /* scan to the end */
                   while( last_DNS->next ) last_DNS = last_DNS->next;
                }
                else {
                   Malloc( Data_Name_Seq, s );
                   if( next_path[k]->tag == NAME ) {
                      s->tag = NAME;
                      s->node.name = next_data->name;
                      s->next = NULL;
                   }
                   else if( next_path[k]->tag == SUB_LOOP ) {
                      s->tag = SUB_LOOP;
                      s->node.sub_loop = NULL;
                      s->next = NULL;
                   }
                   else
                      ERROR_MSG("Unknown data type.");
                   /* the first time add it as next, from then on sub_loop */
                   if( first_diff ) {
                      last_DNS->next = s;
                      first_diff = FALSE;
                      last_DNS = last_DNS->next;
                   }
                   else {
                      last_DNS->node.sub_loop = s;
                      last_DNS = last_DNS->node.sub_loop;
                   }
                }
             }
             /* step things along for the next piece of data */
             if( save_last_DNS->next )
                last_DNS = save_last_DNS = save_last_DNS->next;
             else
                last_DNS = save_last_DNS;
             next_data = next_data->next;
             free( prev_path );
             prev_path = next_path;
         }
         free( prev_path );

         /* print to a temporary file */
         if( tmpnam(tmp_fname) ) 
            if( (tmp_fp = fopen(tmp_fname, "w")) ) {
               /* print out the loop definition */
               i = fprint_Data_Name_Seq( tmp_fp, tmp_DNS, 0 );

               /* print out the data */
               if( (n = fprint_Loop_Data( tmp_fp, tmp_DNS, tmp, 1)) >= 0) {
                  DEBUG_MSG("fprint_Data_Seq(): ran out of data in the loop.");
                  /* tidy up temporary file */
                  fclose( tmp_fp );
                  unlink( tmp_fname );
               } else
                  fclose( tmp_fp );
            } else {
               ERROR_MSG("fprint_Data_Seq(): Could not open tmp_fp.");
               n = -1;
            }
         else {
            ERROR_MSG("fprint_Data_Seq(): Could not create tmp_fname.");
            n = -1;
         }
      } /* go back and try again with a shorter loop */

         /* flush contents of the temporary file through to the output fp */
         if( (tmp_fp = fopen(tmp_fname, "r")) ) {
            char	buffer[512];

            /* terminate the buffer, just in case */
            buffer[511] = '\0';
            while( fgets( buffer, 512, tmp_fp) )
               fputs( buffer, fp);                 /*<<<<<<<<< look*/
            fclose( tmp_fp );
            unlink( tmp_fname );
         } else
            ERROR_MSG("fprint_Data_Seq(): Could not open tmp_fp.");

         add_padding = TRUE;
         /* start the next line at the beginning */
         sb_fputc( '\n', fp);
         free( tmp );
         /* push t along to the last piece of data in the list */
         t = last;
         }
         break;

      case SAVE_BLOCK:
         /* <new-line> */
         /* save_heading <new-line> */
/*       i = fprintf( fp, "\n%s\n", t->name);  */
         i = sb_fputc( '\n'   , fp);
	 i = sb_fputs( t->name, fp);
	 i = sb_fputc( '\n'   , fp);
         /* data */
         i = fprint_Data_Seq( fp, t->data.save_block);
         /* end token */
         i = sb_fputs( "save_", fp );
         i = sb_fputc( '\n', fp );
         add_padding = TRUE;
         t = t->next;
         break;

      default:
         ERROR_MSG("fprint_Data_Seq: Unknown data in Data Sequence.");
         t = t->next;
      } /* end case switch */
   } /* loop for ( t == NULL ) */
   /* pass back error result */
   return( i);
} /* fprint_Data_Seq */

int
fprint_Block_Seq(
register FILE		*fp,
register Block_Seq	*t )
{
int	i;

   /* Scan the entire sequence of blocks */
   for( ; t; t= t->next) {
      /* Print the block's name */
      /* block_heading <new-line> */
/*    i= fprintf( fp, "%s\n", t->name);  */
      i = sb_fputs( t->name, fp );
      i = sb_fputc( '\n'   , fp );
      /* Determine the block's type */
      switch( t->tag ) {

      case DATA_BLOCK:
         i= fprint_Data_Seq( fp, t->block.data_block.actual_data);
         break;

      case GLOBAL_BLOCK:
         i= fprint_Data_Seq( fp, t->block.global_block);
         break;

      default:
         ERROR_MSG("fprint_Block_Seq: Unknown block in Block Sequence.");
      } /* end case switch */
   } /* loop for ( t == NULL ) */
   /* pass back error result */
   return( i);
} /* fprint_Block_Seq */
