/* @(#)expand.c	1.4	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 "sf_space.h"
#include "string.h"
#include "diagmesg.h"
#include "utilitys.h"
#include "marker.h"
	
/* loop_index
 * Update index with the array holding the loop index of the given Data Item 
   from the List Value Sequence.
 * Return the length of the loop index including the terminating zero.
 * NOTE NB ??? 
 * The use of a loop index places a limit of maxint on the number of elements 
   in a list.
 */
int	loop_index(
Data_Item	*data,
List_Value_Seq	*list,
int		**index)
{
   int	i = 1;
   int	*tmp;

   for( ; list; list = list->next)
      switch( list->tag ) {
      case DATA_ITEM:
           if( list->element.data_item == data ) {
              tmp = (int *) malloc( 2 * sizeof(int) ); /* CHANGE 10/3 */
              tmp[0] = i;
              tmp[1] = 0; /* terminate the index */
              *index = tmp;
              return( 2 );
           } else
              i++;
           break;

      case SUB_LOOP: {
           int	len;

           if( len = loop_index( data, list->element.sub_loop, &tmp) ) {
              int	*tmp2;

              tmp2 = (int *) malloc( (len +1) * sizeof(int) );  /* CHANGE 10/3 */
              /* push this index on */
              tmp2[0] = i;
              /* push the sub index on the end */
              for( i = 1; i <= len; i++) tmp2[i] = tmp[ i -1 ];
              free(tmp);
              *index = tmp2;
              return(len+1);
           } else
              i++;
           } break;

      default:
           ERROR_MSG("loop_index(): Unknown domain in LVS.");
      }

  return( 0 );
} /* loop_index */

/* Mark_matching_index
 */
void	Mark_matching_index(
int		*index,
List_Value_Seq	*values,
Data_Item       **Root_Data_Item,
char            **Frame_Codes)
{
  register int	i;

  if( index[0] ) {
     /* skip to the data indicated by values[0] */
     for( i = 1; i < index[0]; i++)
        if( values )
           values = values->next;
        else
           ERROR_MSG("Mark_matching_index(): The number of values don't match the length of the index.");

     /* mark this one */
     if( values )
        switch( values->tag ) {
        case DATA_ITEM:
             Mark_Data_Item( values->element.data_item, Root_Data_Item );
             if( values->element.data_item->tag == FRAME_CODE_DI )
                /* save the reference for later */
                (void)tsearch(values->element.data_item->value,Frame_Codes,strcmp);
             break;

        case SUB_LOOP:
             Mark_matching_index( index +1, values->element.sub_loop,
                                  Root_Data_Item,Frame_Codes);
             break;

        default:
             ERROR_MSG("Mark_LVS(): Unknown domain in LVS.");

        }
     else
        ERROR_MSG("Mark_matching_index(): The number of values don't match the length of the index.");
  } else
     /* mark the whole rest of the sequence */
     Mark_LVS( values, Root_Data_Item, Frame_Codes);
} /* Mark_matching_index */

/* Mark_loop_packet
 */
void	Mark_loop_packet(
List_Value_Seq	*orig_list,
int		full_list,		/* this is an index to the full list of
                         		   values of orig_list */
List_Value_Seq	**values,
Data_Item       **Root_Data_Item,
char            **Frame_Codes)
{
   register List_Value_Seq	*list;

   for( list = orig_list; list; list = list->next)
      switch( list->tag ) {
      case DATA_ITEM: {
           register int	i = 0;
           int		*index;

           /* get the loop index for this data item */
           (void)loop_index( list->element.data_item, values[full_list],&index);
           while( values[i] )
              Mark_matching_index( index, values[i++],Root_Data_Item,Frame_Codes);
           } break;

      case SUB_LOOP:
           Mark_loop_packet(list->element.sub_loop, full_list, values, 
                            Root_Data_Item, Frame_Codes);
           break;

      default:
           ERROR_MSG("Mark_loop_packet(): Unknown domain in LVS.");
      }
} /* Mark_loop_packet */

/* Mark_LVS_loop_packets
 */
void	Mark_LVS_loop_packets(
Data_Seq	*pos,
Data_Seq	*last,
Data_Item       **Root_Data_Item,
char            **Frame_Codes)
{
  List_Value_Seq **tmp;
  int	n, k, full_list;
  Boolean	in_the_same_loop, have_not_reached_the_loop;

  n = Number_of_Names( pos->data.loop.packet_members );
  /* build array of pointers to values we wish to search */
  tmp = (List_Value_Seq **)malloc( n * sizeof(List_Value_Seq *));
  full_list = k = 0;
  in_the_same_loop = FALSE;
  have_not_reached_the_loop = TRUE;
  while( in_the_same_loop || have_not_reached_the_loop )
     if( last ) {
        if( last->tag == LOOP )
           if(pos->data.loop.packet_members == last->data.loop.packet_members) {
              if( ! strcmp( pos->name, last->name ) )
                 /* this is the orignal data for this loop being processed */
                 full_list = k;
              tmp[k++] = last->data.loop.values;
              have_not_reached_the_loop = FALSE;
              in_the_same_loop = TRUE;
           }
           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;
        last = last->next;
     } else      /* no more data at all */
        have_not_reached_the_loop = in_the_same_loop = FALSE;
  /* shrink the block down to only the size it needs to be */
  n = k +1;
  tmp = (List_Value_Seq **)realloc(tmp, n * sizeof(List_Value_Seq *));
  tmp[ n -1 ] = NULL;

  /* find all the data in the original file */
  Mark_loop_packet( pos->data.loop.values, full_list, tmp, Root_Data_Item,
                    Frame_Codes);
} /* Mark_LVS_loop_packets */

/* expand_DS_loop_packets
 */
Data_Seq	*expand_DS_loop_packets(
Data_Seq	*base,
Data_Seq	*file)
{
   register Data_Seq    *pos = NULL;
   Data_Item    *Root_Data_Item = NULL;
   Data_Seq     *rtn_ds = NULL;
   char *frame_codes = NULL;
   char *dummy = NULL;
   char *known_save_blocks = NULL;

  /* scan along the base marking the data required */
  for( pos = base; pos; pos = pos->next)
     switch( pos->tag ) {
     case DATA_ITEM:
          Mark_Data_Item( pos->data.data_item, &Root_Data_Item );
          break;

     case LOOP:
          Mark_LVS_loop_packets( pos, file, &Root_Data_Item, &frame_codes);
          break;

     case SAVE_BLOCK: {
          register Data_Seq	*d = pos->data.save_block;
          register Data_Seq     *t = file;
          Data_Seq		*save_ds = NULL;

          /* find matching save block in the file */
          while( t )
             switch( t->tag ) {
             case DATA_ITEM:
             case LOOP:
                t = t->next;
                break;
             case SAVE_BLOCK:
                if( ! strcmp( pos->name, t->name) ) {
                   save_ds = t->data.save_block;
                   t = NULL;
                }
                else
                   t = t->next;
                break;
             default:
              ERROR_MSG("expand_DS_save_frames(): unknown domain in sequence.");
              t = t->next;
             }

          /* do the same sort of thing for the save block */
          for( ; d; d = d->next )
             switch( d->tag ) {
             case DATA_ITEM:
                Mark_Data_Item( d->data.data_item, &Root_Data_Item );
                break;
             case LOOP:
                Mark_LVS_loop_packets( d, save_ds,&Root_Data_Item,&frame_codes);
                break;
             default:
                ERROR_MSG("expand_loop_packets(): unknown domain in sequence.");
             }
          /* record this block as known */
          (void)tsearch( pos->name, &known_save_blocks, strcmp);
          } break;

     default:
          ERROR_MSG("expand_loop_packets(): unknown domain in sequence.");
     }

  /* Finally, cross reference so that all Frame Codes are resolved */
  refs_by_frame_codes(file,&Root_Data_Item,&frame_codes,&dummy,
                      &known_save_blocks);
     
  /* construct sequence of marked data_items */
  rtn_ds = get_Marked_DS( file, &Root_Data_Item, &known_save_blocks );
  return( rtn_ds );
} /* expand_DS_loop_packets */

/* expand_loop_packets
 */
Block_Seq	*expand_loop_packets(
Block_Seq	*base,
Block_Seq	*file)
{
  register Block_Seq	*last_bs = NULL;
  Block_Seq		*tmp_bs = NULL;
  Global_Seq		*globals = NULL;
  Global_Seq		*tmp_globals = NULL;

  for(; base; base = base->next) {
     register Block_Seq	*b = file;

     /* search for matching block */
     while( b )
        if( ! strcmp( base->name, b->name) ) {
           /* build a fresh node, with the old data */
           switch( b->tag ) {
           case DATA_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = DATA_BLOCK;
                last_bs->block.data_block.actual_data = 
                  expand_DS_loop_packets( base->block.data_block.actual_data,
                                             b->block.data_block.actual_data);
                last_bs->block.data_block.global_data = globals;
                last_bs->next = NULL;
              break;

           case GLOBAL_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = GLOBAL_BLOCK;
                last_bs->block.global_block =
                            expand_DS_loop_packets( base->block.global_block,
                                                       b->block.global_block);
                last_bs->next = NULL;

                /* add this to the head of the list of globals */
                Malloc( Global_Seq, tmp_globals );
                tmp_globals->global_entry = last_bs;
                tmp_globals->next = globals;
                globals = tmp_globals;
                break;

           default:
             ERROR_MSG("expand_loop_packets(): unknown block in Block_Seq.");

           }
           /* terminate the search */
           b = NULL;
        } else 
           /* continue the search */
           b = b->next;
  }
  return( tmp_bs );
} /* expand_loop_packets */

/* Mark_packet_members
 */
void	Mark_packet_members(
Data_Name_Seq	*names,
Data_Seq	*file,
Data_Item	**root,
char		**frame_codes)
{
   for(; names; names = names->next) 
      switch( names->tag ) {
      case NAME: {
          /* find same name in the file */
          register Data_Seq     *t = file;

          /* find matching loop data in the file */
          while( t )
             switch( t->tag ) {
             case LOOP:
                if( ! strcmp( names->node.name, t->name) ) {
                   /* mark all of its data */
                   Mark_LVS( t->data.loop.values, root, frame_codes);
                   t = NULL;
                }
                else
                   t = t->next;
                break;

             case DATA_ITEM:
             case SAVE_BLOCK:
                t = t->next;
                break;
             default:
              ERROR_MSG("expand_DS_save_frames(): unknown domain in sequence.");
              t = t->next;
             }
         } break;

      case SUB_LOOP:
         Mark_packet_members( names->node.sub_loop, file, root, frame_codes);
         break;

      default:
         ERROR_MSG("Mark_packet_members(): Unknown data in Data Name Sequence");
      }
  return;
} /* Mark_packet_members */

/* expand_DS_loop_structures
 */
Data_Seq	*expand_DS_loop_structures(
Data_Seq	*base,
Data_Seq	*file)
{
   register Data_Seq    *pos = NULL;
   Data_Item    *Root_Data_Item = NULL;
   Data_Seq     *rtn_ds = NULL;
   char *frame_codes = NULL;
   char *dummy = NULL;
   char *known_save_blocks = NULL;

  /* scan along the base marking the data required */
  for( pos = base; pos; pos = pos->next)
     switch( pos->tag ) {
     case DATA_ITEM:
          Mark_Data_Item( pos->data.data_item, &Root_Data_Item );
          break;

     case LOOP: 
          /* find all the data in the original file */
          Mark_packet_members( pos->data.loop.packet_members, file,
                               &Root_Data_Item, &frame_codes);
          break;

     case SAVE_BLOCK: {
          register Data_Seq	*d = pos->data.save_block;
          register Data_Seq     *t = file;
          Data_Seq		*save_ds = NULL;

          /* find matching save block in the file */
          while( t )
             switch( t->tag ) {
             case DATA_ITEM:
             case LOOP:
                t = t->next;
                break;
             case SAVE_BLOCK:
                if( ! strcmp( pos->name, t->name) ) {
                   save_ds = t->data.save_block;
                   t = NULL;
                }
                else
                   t = t->next;
                break;
             default:
              ERROR_MSG("expand_DS_save_frames(): unknown domain in sequence.");
              t = t->next;
             }

          /* do the same sort of thing for the save block */
          for( ; d; d = d->next )
             switch( d->tag ) {
             case DATA_ITEM:
                Mark_Data_Item( d->data.data_item, &Root_Data_Item );
                break;
             case LOOP:
                /* find the save block in the file */
                Mark_packet_members( d->data.loop.packet_members, save_ds,
                                     &Root_Data_Item, &frame_codes);
                break;
             default:
                ERROR_MSG("expand_loop_structures(): unknown domain in sequence.");
             }
          /* record this block as known */
          (void)tsearch( pos->name, &known_save_blocks, strcmp);
          } break;

     default:
          ERROR_MSG("expand_loop_structures(): unknown domain in sequence.");
     }

  /* Finally, cross reference so that all Frame Codes are resolved */
  refs_by_frame_codes(file,&Root_Data_Item,&frame_codes, &dummy,
                      &known_save_blocks);
     
  /* construct sequence of marked data_items */
  rtn_ds = get_Marked_DS( file, &Root_Data_Item, &known_save_blocks );
  return( rtn_ds );
} /* expand_DS_loop_structures */

/* expand_loop_structures
 */
Block_Seq	*expand_loop_structures(
Block_Seq	*base,
Block_Seq	*file)
{
  register Block_Seq	*last_bs = NULL;
  Block_Seq		*tmp_bs = NULL;
  Global_Seq		*globals = NULL;
  Global_Seq		*tmp_globals = NULL;

  for(; base; base = base->next) {
     register Block_Seq	*b = file;

     /* search for matching block */
     while( b )
        if( ! strcmp( base->name, b->name) ) {
           /* build a fresh node, with the old data */
           switch( b->tag ) {
           case DATA_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = DATA_BLOCK;
                last_bs->block.data_block.actual_data = 
                  expand_DS_loop_structures( base->block.data_block.actual_data,
                                             b->block.data_block.actual_data);
                last_bs->block.data_block.global_data = globals;
                last_bs->next = NULL;
              break;

           case GLOBAL_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = GLOBAL_BLOCK;
                last_bs->block.global_block =
                            expand_DS_loop_structures( base->block.global_block,
                                                       b->block.global_block);
                last_bs->next = NULL;

                /* add this to the head of the list of globals */
                Malloc( Global_Seq, tmp_globals );
                tmp_globals->global_entry = last_bs;
                tmp_globals->next = globals;
                globals = tmp_globals;
                break;

           default:
             ERROR_MSG("expand_loop_structures(): unknown block in Block_Seq.");

           }
           /* terminate the search */
           b = NULL;
        } else 
           /* continue the search */
           b = b->next;
  }
  return( tmp_bs );
} /* expand_loop_structures */

/* expand_DS_save_frames
 * Expand any save frames occuring in the base sequence of data.
 * Return the fresh data_sequence.
 */
Data_Seq	*expand_DS_save_frames(
Data_Seq	*base,
Data_Seq	*file)
{
   register Data_Seq    *pos = NULL;
   Data_Item    *Root_Data_Item = NULL;
   Data_Seq     *rtn_ds = NULL;
   char *frame_codes = NULL;
   char *dummy = NULL;
   char *known_save_blocks = NULL;

  /* scan along the base marking the data required */
  for( pos = base; pos; pos = pos->next)
     switch( pos->tag ) {
     case DATA_ITEM:
          Mark_Data_Item( pos->data.data_item, &Root_Data_Item );
          break;

     case LOOP: {
          char	*dumb_char_ptr = NULL;

          Mark_LVS( pos->data.loop.values, &Root_Data_Item, &dumb_char_ptr);
          } break;

     case SAVE_BLOCK: {
          register Data_Seq	*d = NULL;
          register Data_Seq     *t = file;

          /* find matching save block in the file */
          while( t )
             switch( t->tag ) {
             case DATA_ITEM:
             case LOOP:
                t = t->next;
                break;
             case SAVE_BLOCK:
                if( ! strcmp( pos->name, t->name) ) {
                   d = t->data.save_block;
                   t = NULL;
                }
                else
                   t = t->next;
                break;
             default:
              ERROR_MSG("expand_DS_save_frames(): unknown domain in sequence.");
              t = t->next;
             }

          /* mark the whole contents of the save block */
          for( ; d; d = d->next )
             switch( d->tag ) {
             case DATA_ITEM:
                Mark_Data_Item( d->data.data_item, &Root_Data_Item );
                if( d->data.data_item->tag == FRAME_CODE_DI )
                   /* save the reference for later */
                   (void)tsearch(d->data.data_item->value,&frame_codes,strcmp);
                break;
             case LOOP:
                Mark_LVS(d->data.loop.values,&Root_Data_Item,&frame_codes);
                break;
             default:
                ERROR_MSG("names_in_Data_Seq: unknown domain in sequence.");
             }
          /* record this block as known */
          (void)tsearch( pos->name, &known_save_blocks, strcmp);
          } break;

     default:
          ERROR_MSG("expand_DS_save_frames(): unknown domain in sequence.");
     }

  /* Finally, cross reference so that all Frame Codes are resolved */
  refs_by_frame_codes(file,&Root_Data_Item,&frame_codes, &dummy,
                      &known_save_blocks);
     
  /* construct sequence of marked data_items */
  rtn_ds = get_Marked_DS( file, &Root_Data_Item, &known_save_blocks );
  return( rtn_ds );
} /* expand_DS_save_frames */

/* expand_save_frames
 * Scan along the base, expanding the Save Frames within each Data Sequence.
 */
Block_Seq	*expand_save_frames(
Block_Seq	*base,
Block_Seq	*file)
{
  register Block_Seq	*last_bs = NULL;
  Block_Seq		*tmp_bs = NULL;
  Global_Seq		*globals = NULL;
  Global_Seq		*tmp_globals = NULL;

  for(; base; base = base->next) {
     register Block_Seq	*b = file;

     /* search for matching block */
     while( b )
        if( ! strcmp( base->name, b->name) ) {
           /* build a fresh node, with the old data */
           switch( b->tag ) {
           case DATA_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = DATA_BLOCK;
                last_bs->block.data_block.actual_data = 
                      expand_DS_save_frames( base->block.data_block.actual_data,
                                             b->block.data_block.actual_data);
                last_bs->block.data_block.global_data = globals;
                last_bs->next = NULL;
              break;

           case GLOBAL_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = GLOBAL_BLOCK;
                last_bs->block.global_block =
                                expand_DS_save_frames( base->block.global_block,
                                                       b->block.global_block);
                last_bs->next = NULL;

                /* add this to the head of the list of globals */
                Malloc( Global_Seq, tmp_globals );
                tmp_globals->global_entry = last_bs;
                tmp_globals->next = globals;
                globals = tmp_globals;
                break;

           default:
              ERROR_MSG("expand_save_frames(): unknown block in Block_Seq.");

           }
           /* terminate the search */
           b = NULL;
        } else 
           /* continue the search */
           b = b->next;
  }
  return( tmp_bs );
} /* expand_save_frames */

/* expand_data_blocks
 * Search along the base expanding each block,
 * with the full block from the file.
 * Return the expanded block sequence.
 */
Block_Seq	*expand_data_blocks(
Block_Seq	*base,
Block_Seq	*file)
{
  register Block_Seq	*last_bs = NULL;
  Block_Seq		*tmp_bs = NULL;
  Global_Seq		*globals = NULL;
  Global_Seq		*tmp_globals = NULL;

  for(; base; base = base->next) {
     register Block_Seq	*b = file;

     /* search for matching block */
     while( b )
        if( ! strcmp( base->name, b->name) ) {
           /* build a fresh node, with the old data */
           switch( b->tag ) {
           case DATA_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = DATA_BLOCK;
                last_bs->block.data_block.actual_data = b->block.data_block.actual_data;
                last_bs->block.data_block.global_data = globals;
                last_bs->next = NULL;
              break;

           case GLOBAL_BLOCK:
                if( last_bs )
                   { Malloc( Block_Seq, last_bs->next ); last_bs = last_bs->next; }
                else
                   { Malloc( Block_Seq, last_bs ); tmp_bs = last_bs; }
                last_bs->name = b->name;
                last_bs->tag = GLOBAL_BLOCK;
                last_bs->block.global_block = b->block.global_block;
                last_bs->next = NULL;

                /* add this to the head of the list of globals */
                Malloc( Global_Seq, tmp_globals );
                tmp_globals->global_entry = last_bs;
                tmp_globals->next = globals;
                globals = tmp_globals;
                break;

           default:
              ERROR_MSG("expand_data_blocks(): unknown block in Block_Seq.");

           }
           /* terminate the search */
           b = NULL;
        } else 
           /* continue the search */
           b = b->next;
  }
  return( tmp_bs );
} /* expand_data_blocks */


