/*
 * Copyright (c) Tony Bybell 1999-2016.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */

/* AIX may need this for alloca to work */
#if defined _AIX
  #pragma alloca
#endif

#include "globals.h"
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symbol.h"
#include "lxt.h"
#include "debug.h"
#include "bsearch.h"
#include "strace.h"
#include "translate.h"
#include "ptranslate.h"
#include "ttranslate.h"
#include "hierpack.h"
#include "analyzer.h"


void UpdateTraceSelection(Trptr t);
int traverse_vector_nodes(Trptr t);

/*
 * extract last n levels of hierarchy
 */
char *hier_extract(char *pnt, int levels)
{
int i, len;
char ch, *pnt2, *esc;
char only_nums_so_far=1;

if(!pnt) return(NULL);

len=strlen(pnt);
if(!len) return(pnt);

if(levels<1) levels=1;
if((!GLOBALS->hier_ignore_escapes) &&(esc=strchr(pnt, '\\')))
	{
	return((levels==1) ? esc : pnt); /* shortcut out on escape IDs: level=1, esc char else all */
	}

pnt2=pnt+len-1;
/* ch=*pnt2; */ /* scan-build : useless given loop below */

for(i=0;i<len;i++)
	{
	ch=*(pnt2--);
	if((only_nums_so_far)&&(ch>='0')&&(ch<='9'))		/* skip 1st set of signal.number hier from right if it exists */
		{
		continue;
		/* nothing */
		}
	else
		{
		if(ch==GLOBALS->hier_delimeter)
			{
			if(!only_nums_so_far) levels--;
			if(!levels)
				{
				pnt2+=2;
				return(pnt2);
				}
			}
		only_nums_so_far=0;
		}
	}

return(pnt); /* not as many levels as max, so give the full name.. */
}

void updateTraceGroup(Trptr t)
{
  /*  t->t_match = NULL; */

  if (t->t_prev)
    {
      if (IsGroupBegin(t->t_prev))
	{
	  if (IsGroupEnd(t))
	    { /* empty group */
	      Trptr g_begin = t->t_prev;
	      t->t_grp = g_begin->t_grp;
	      t->t_match = g_begin;
	      g_begin->t_match = t;
	    }
	  else
	    { /* first trace in group */
	      t->t_grp = t->t_prev;
	    }
	}
      else
	{
	  if (IsGroupEnd(t))
	    {
	      Trptr g_begin = t->t_prev->t_grp;
	      if(g_begin) /* scan-build */
		{
	      	t->t_grp = g_begin->t_grp;
	      	t->t_match = g_begin;
	      	g_begin->t_match = t;
		}
	    }
	  else
	    {
	      t->t_grp = t->t_prev->t_grp;
	    }


	}
    }
  else
    { /* very first trace */
      t->t_grp = NULL;
    }

  if ((t->t_grp) && IsSelected(t->t_grp))
    {
      t->flags |= TR_HIGHLIGHT;
    }
}

void CloseTrace(Trptr t)
{
GLOBALS->traces.dirty = 1;

  if (IsGroupBegin(t))
    {
      t->flags |= TR_CLOSED;
      if (t->t_match) { t->t_match->flags |= TR_CLOSED; };

      if (!HasWave(t))
	{
	  /* Group End */
	  if (t->t_match) { t->t_match->flags |= TR_COLLAPSED; };
	}
      else
	{
	  /* Composite  End */
	  if (t->t_match) { t->t_match->flags |= TR_COLLAPSED; };
	}
    }

  if (IsGroupEnd(t))
    {
      t->flags |= TR_CLOSED;
      if (t->t_match) { t->t_match->flags |= TR_CLOSED; };

      if ((t->t_match) && !HasWave(t->t_match))
	{
	  /* Group End */
	  t->flags |= TR_COLLAPSED;
	}
      else
	{
	  /* Composite End */
	  t->flags |= TR_COLLAPSED;
	}
    }
}


void OpenTrace(Trptr t)
{
GLOBALS->traces.dirty = 1;

  if (IsGroupBegin(t) || IsGroupEnd(t))
  {
    t->flags &= ~TR_CLOSED;
    if (t->t_match) { t->t_match->flags &= ~TR_CLOSED; };

    if (!HasWave(t))
      {
	t->flags &= ~TR_COLLAPSED;
	if(t->t_match) { t->t_match->flags &= ~TR_COLLAPSED; };
      }
  }
}

void ClearTraces(void)
{
  Trptr t = GLOBALS->traces.first;
  while(t)
	{
	  t->flags &= ~TR_HIGHLIGHT;
	  t=t->t_next;
	}
  GLOBALS->traces.dirty = 1;
}

void ClearGroupTraces(Trptr t_grp)
{
  if (IsGroupBegin(t_grp))
    {
      Trptr t = t_grp;
      while(t)
	{
	  t->flags &= ~TR_HIGHLIGHT;
	  if(t->t_match == t_grp) break;
	  t=t->t_next;
	}
    GLOBALS->traces.dirty = 1;
    }
  else
    {
      fprintf(stderr, "INTERNAL ERROR: ClearGroupTrace applied to non-group!  Exiting.\n");
      exit(255);
    }
}

void MarkTraceCursor(Trptr t_curs)
{
if(t_curs)
	{
	Trptr t=GLOBALS->traces.first;

	while(t)
		{
		t->is_cursor = 0;
		t=t->t_next;
		}

	t_curs->is_cursor = 1;
	}
}

/*
 * Add a trace to the display...
 */
static void AddTrace( Trptr t )
{
GLOBALS->traces.dirty = 1;

t->t_fpdecshift = GLOBALS->default_fpshift;

if((GLOBALS->which_t_color > 0) && (GLOBALS->which_t_color <= WAVE_NUM_RAINBOW))
	{
	t->t_color = GLOBALS->which_t_color;
	GLOBALS->which_t_color = 0;
	}

if(GLOBALS->default_flags&TR_NUMMASK) t->flags=GLOBALS->default_flags;
	else t->flags=(t->flags&TR_NUMMASK)|GLOBALS->default_flags;

if(!t->vector && GLOBALS->enum_nptrs_jrb)
	{
	JRB enum_nptr = jrb_find_vptr(GLOBALS->enum_nptrs_jrb, t->n.nd);
	if(enum_nptr)
		{
		int e_filter = enum_nptr->val.ui;
		if((e_filter > 0) && (e_filter <= GLOBALS->num_xl_enum_filter))
			{
			t->e_filter = e_filter;
			if(!(GLOBALS->default_flags&TR_NUMMASK)) t->flags = (t->flags & (~TR_NUMMASK)) | TR_ENUM | TR_BIN; /* need to downgrade to bin to make visible */
			}
		}
	}

if(GLOBALS->default_flags & TR_FTRANSLATED)
	{
	t->f_filter = GLOBALS->current_translate_file;
	}
else
if(GLOBALS->default_flags & TR_PTRANSLATED)
	{
	t->p_filter = GLOBALS->current_translate_proc;
	}

/* NOT an else! */
if(GLOBALS->default_flags & TR_TTRANSLATED)
	{
	t->t_filter = GLOBALS->current_translate_ttrans;
	if(t->t_filter)
		{
		if(!t->vector)
			{
			bvptr v;
			TraceFlagsType cache_hi = t->flags & TR_HIGHLIGHT;

			t->flags |= TR_HIGHLIGHT;
                        v = combine_traces(1, t); /* down: make single signal a vector */
                      	if(v)
                              	{
                                v->transaction_nd = t->n.nd; /* cache for savefile, disable */
                                t->vector = 1;
                             	t->n.vec = v;           /* splice in */
                               	}

			t->flags &= ~TR_HIGHLIGHT;
			t->flags |= cache_hi;
			}

		if(GLOBALS->ttranslate_args)
                	{
                        t->transaction_args = strdup_2(GLOBALS->ttranslate_args);
                        }
                        else
                        {
                        t->transaction_args = NULL;
                        }

		traverse_vector_nodes(t);
		}
		else
		{
		t->flags &= ~TR_TTRANSLATED; /* malformed filter syntax?  should never have "which" of zero here */
		}
	}

 if (IsGroupBegin(t)) {
   GLOBALS->group_depth = GLOBALS->group_depth + 1;
 }

 if (IsGroupEnd(t)) {
   if (GLOBALS->group_depth == 0) {
     fprintf(stderr, "ERROR: Group End encountered with no matching start. Ignoring.\n");
     t->flags &= ~TR_GRP_END;
   } else {
     GLOBALS->group_depth = GLOBALS->group_depth - 1;
   }
 }

if(GLOBALS->shift_timebase_default_for_add)
	t->shift=GLOBALS->shift_timebase_default_for_add;

if(!GLOBALS->strace_ctx->shadow_active)
	{
	if( GLOBALS->traces.first == NULL )
		{
		t->t_next = t->t_prev = NULL;
		GLOBALS->traces.first = GLOBALS->traces.last = t;
	      	}
	    	else
	      	{
		t->t_next = NULL;
		t->t_prev = GLOBALS->traces.last;
		GLOBALS->traces.last->t_next = t;
		GLOBALS->traces.last = t;
	      	}
	GLOBALS->traces.total++;
	updateTraceGroup(GLOBALS->traces.last);
	}
	else	/* hide offscreen */
	{
	struct strace *st = calloc_2(1, sizeof(struct strace));
	st->next = GLOBALS->strace_ctx->shadow_straces;
	st->value = GLOBALS->strace_ctx->shadow_type;
	st->trace = t;

	st->string = GLOBALS->strace_ctx->shadow_string; /* copy string over */
	GLOBALS->strace_ctx->shadow_string = NULL;

	GLOBALS->strace_ctx->shadow_straces = st;
	}
}


/*
 * Add a blank trace to the display...
 */
static char *precondition_string(char *s)
{
int len=0;
char *s2;

if(!s) return(NULL);
s2=s;
while((*s2)&&((*s2)!='\n')&&((*s2)!='\r'))	/* strip off ending CR/LF */
	{
	len++;
	s2++;
	}
if(!len) return(NULL);
s2=(char *)calloc_2(1,len+1);
memcpy(s2,s,len);
return(s2);
}

int AddBlankTrace(char *commentname)
{
  Trptr  t;
  char *comment;
  TraceFlagsType flags_filtered;

  if( (t = (Trptr) calloc_2( 1, sizeof( TraceEnt ))) == NULL )
    {
      fprintf( stderr, "Out of memory, can't add blank trace to analyzer\n");
      return( 0 );
    }
  AddTrace(t);
  /* Keep only flags that make sense for a blank trace. */
  flags_filtered = TR_BLANK | (GLOBALS->default_flags & (TR_CLOSED|
							 TR_GRP_BEGIN|
							 TR_GRP_END|
							 TR_COLLAPSED|
							 TR_ANALOG_BLANK_STRETCH));
  t->flags = flags_filtered;
  if(t->flags & TR_ANALOG_BLANK_STRETCH)

    {
      t->flags &= ~TR_BLANK;
    }

  if((comment=precondition_string(commentname)))
    {
      t->name      = comment;
    }

  return(1);
}


/*
 * Insert a blank [or comment] trace into the display...
 */
int InsertBlankTrace(char *comment, TraceFlagsType different_flags)
{
TempBuffer tb;
char *comm;
Trptr  t;

if( (t = (Trptr) calloc_2( 1, sizeof( TraceEnt ))) == NULL )
	{
	fprintf( stderr, "Out of memory, can't insert blank trace to analyzer\n");
	return( 0 );
      	}

GLOBALS->traces.dirty = 1;

if(!different_flags)
	{
	t->flags=TR_BLANK;
	}
	else
	{
	t->flags = different_flags;
	}

if((comm=precondition_string(comment)))
	{
	t->name = comm;
	}

if(!GLOBALS->traces.first)
	{
	GLOBALS->traces.first=GLOBALS->traces.last=t;
	GLOBALS->traces.total=1;
	return(1);
	}
	else
	{
	tb.buffer=GLOBALS->traces.buffer;
	tb.bufferlast=GLOBALS->traces.bufferlast;
	tb.buffercount=GLOBALS->traces.buffercount;

	GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=t;
	GLOBALS->traces.buffercount=1;
	PasteBuffer();

	GLOBALS->traces.buffer=tb.buffer;
	GLOBALS->traces.bufferlast=tb.bufferlast;
	GLOBALS->traces.buffercount=tb.buffercount;

	return(1);
	}
}


/*
 * Adds a single bit signal to the display...
 */
int AddNodeTraceReturn(nptr nd, char *aliasname, Trptr *tret)
{
  Trptr  t;
  hptr histpnt;
  hptr *harray;
  int histcount;
  int i;

  if(!nd) return(0); /* passed it a null node ptr by mistake */
  if(nd->mv.mvlfac) import_trace(nd);

  GLOBALS->signalwindow_width_dirty=1;
  GLOBALS->traces.dirty = 1;

  if( (t = (Trptr) calloc_2( 1, sizeof( TraceEnt ))) == NULL )
    {
      fprintf( stderr, "Out of memory, can't add to analyzer\n" );
      return( 0 );
    }

  if(!nd->harray)		/* make quick array lookup for aet display */
    {
      histpnt=&(nd->head);
      histcount=0;

      while(histpnt)
	{
	  histcount++;
	  histpnt=histpnt->next;
	}

      nd->numhist=histcount;

      if(!(nd->harray=harray=(hptr *)malloc_2(histcount*sizeof(hptr))))
	{
	  fprintf( stderr, "Out of memory, can't add to analyzer\n" );
	  free_2(t);
	  return(0);
	}

      histpnt=&(nd->head);
      for(i=0;i<histcount;i++)
	{
	  *harray=histpnt;
	  harray++;
	  histpnt=histpnt->next;
	}
    }

  if(aliasname)
   {
     char *alias;

     t->name_full = alias =(char *)malloc_2(strlen(aliasname)+1);
     strcpy(alias,aliasname);
     t->name = t->name_full;
     if(GLOBALS->hier_max_level)
       t->name = hier_extract(t->name_full, GLOBALS->hier_max_level);
   }
  else
    {
      if(!GLOBALS->hier_max_level)
	{
	  int flagged = HIER_DEPACK_ALLOC;

	  t->name = hier_decompress_flagged(nd->nname, &flagged);
	  t->is_depacked = (flagged != 0);
	}
      else
	{
	  int flagged = HIER_DEPACK_ALLOC;
	  char *tbuff = hier_decompress_flagged(nd->nname, &flagged);
	  if(!flagged)
	    {
	      t->name = hier_extract(nd->nname, GLOBALS->hier_max_level);
	    }
	  else
	    {
	      t->name = strdup_2(hier_extract(tbuff, GLOBALS->hier_max_level));
	      free_2(tbuff);
	      t->is_depacked = 1;
	    }
	}
    }

  if(nd->extvals) /* expansion vectors */
    {
      int n;

      n = nd->msi - nd->lsi;
      if(n<0)n=-n;
      n++;

      switch(nd->vartype)
	{
	case ND_VCD_INTEGER:
	case ND_VCD_PARAMETER:
	case ND_SV_INT:
	case ND_SV_SHORTINT:
	case ND_SV_LONGINT:	t->flags = TR_SIGNED | TR_RJUSTIFY;
				break;

	default: 		t->flags = (( n > 3 )||( n < -3 )) ? TR_HEX|TR_RJUSTIFY : TR_BIN|TR_RJUSTIFY;
				break;
	}
    }
  else
    {
      t->flags |= TR_BIN;	/* binary */
    }
  t->vector = FALSE;
  t->n.nd = nd;
  if(tret) *tret = t;		/* for expand */
  AddTrace( t );
  return( 1 );
}


/* single node */
int AddNode(nptr nd, char *aliasname)
{
return(AddNodeTraceReturn(nd, aliasname, NULL));
}


/* add multiple nodes (if array) */
int AddNodeUnroll(nptr nd, char *aliasname)
{
#ifdef WAVE_ARRAY_SUPPORT
if(nd->array_height <= 1)
#endif
	{
	return(AddNodeTraceReturn(nd, aliasname, NULL));
	}
#ifdef WAVE_ARRAY_SUPPORT
	else
	{
	unsigned int i;
	int rc = 1;

	for(i=0;i<nd->array_height;i++)
		{
		rc |= AddNodeTraceReturn(nd+i, aliasname, NULL);
		}
	return(rc);
	}
#endif
}


/*
 * Adds a vector to the display...
 */
int AddVector(bvptr vec, char *aliasname)
  {
    Trptr  t;
    int    n;

    if(!vec) return(0); /* must've passed it a null pointer by mistake */

    GLOBALS->signalwindow_width_dirty=1;
    GLOBALS->traces.dirty = 1;

    n = vec->nbits;
    t = (Trptr) calloc_2(1, sizeof( TraceEnt ) );
    if( t == NULL )
      {
	fprintf( stderr, "Out of memory, can't add %s to analyzer\n",
	  vec->bvname );
	return( 0 );
      }

    if (aliasname)
      {
	t->name_full = strdup_2(aliasname);
	t->name = t->name_full;
      }
    else
      {
	t->name = vec->bvname;
      }

    if(GLOBALS->hier_max_level)
      t->name = hier_extract(t->name, GLOBALS->hier_max_level);

    t->flags = ( n > 3 ) ? TR_HEX|TR_RJUSTIFY : TR_BIN|TR_RJUSTIFY;
    t->vector = TRUE;
    t->n.vec = vec;
    AddTrace( t );
    return( 1 );
  }


/*
 * Free up a trace's mallocs...
 */
void FreeTrace(Trptr t)
{
GLOBALS->traces.dirty = 1;

if(GLOBALS->starting_unshifted_trace == t)
	{
	GLOBALS->starting_unshifted_trace = NULL; /* for new "standard" clicking routines */
	}

if(GLOBALS->strace_ctx->straces)
	{
	struct strace_defer_free *sd = calloc_2(1, sizeof(struct strace_defer_free));
	sd->next = GLOBALS->strace_ctx->strace_defer_free_head;
	sd->defer = t;

	GLOBALS->strace_ctx->strace_defer_free_head = sd;
	return;
	}

if(t->vector)
      	{
      	bvptr bv, bv2;
	int i;

	bv=t->n.vec;
	/* back out allocation to revert (if any) */
        if(bv->transaction_cache)
		{
		t->n.vec = bv->transaction_cache;

		while(bv)
			{
			bv2 = bv->transaction_chain;
			if(bv->bvname) { free_2(bv->bvname); }

	                for(i=0;i<bv->numregions;i++)
				{
	                        free_2(bv->vectors[i]);
	                        }

	                free_2(bv);
			bv = bv2;
			}

		bv=t->n.vec;
                }

	/* normal vector deallocation */
	for(i=0;i<bv->numregions;i++)
		{
		if(bv->vectors[i]) free_2(bv->vectors[i]);
		}

	if(bv->bits)
		{
		if(bv->bits->name) free_2(bv->bits->name);
		if(bv->bits->attribs) free_2(bv->bits->attribs);
		for(i=0;i<bv->nbits;i++)
			{
			DeleteNode(bv->bits->nodes[i]);
			}
		free_2(bv->bits);
		}

	if(bv->bvname) free_2(bv->bvname);
      	if(t->n.vec) free_2(t->n.vec);
      	}
	else
	{
	if(t->n.nd && t->n.nd->expansion)
		{
		DeleteNode(t->n.nd);
		}
	}

 if(t->is_depacked) free_2(t->name);
 if(t->asciivalue) free_2(t->asciivalue);
 if(t->name_full)  free_2(t->name_full);
 if(t->transaction_args) free_2(t->transaction_args);
 free_2( t );
}


/*
 * Remove a trace from the display and optionally
 * deallocate its memory usage...
 */
void RemoveTrace( Trptr t, int dofree )
  {
    GLOBALS->traces.dirty = 1;
    GLOBALS->traces.total--;
    if( t == GLOBALS->traces.first )
      {
	GLOBALS->traces.first = t->t_next;
	if( t->t_next )
            t->t_next->t_prev = NULL;
        else
            GLOBALS->traces.last = NULL;
      }
    else
      {
        if(t->t_prev)
		{
	        t->t_prev->t_next = t->t_next;
		}
		else
		{
		/* this code should likely *never* execute as if( t == GLOBALS->traces.first ) above should catch this */
		/* there is likely a problem elsewhere in the code! */

		Trptr t2 = GLOBALS->traces.first = t->t_next;
		GLOBALS->traces.total = 0;
		while(t2)
			{
			t2 = t2->t_next;
			GLOBALS->traces.total++;
			}
		}


        if( t->t_next )
            t->t_next->t_prev = t->t_prev;
        else
            GLOBALS->traces.last = t->t_prev;
      }

    if(dofree)
	{
        FreeTrace(t);
	}
  }


/*
 * Deallocate the cut/paste buffer...
 */
void FreeCutBuffer(void)
{
Trptr t, t2;

t=GLOBALS->traces.buffer;

while(t)
	{
	t2=t->t_next;
	FreeTrace(t);
	t=t2;
	}

GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
GLOBALS->traces.buffercount=0;
}


/*
 * Cut highlighted traces from the main screen
 * and throw them in the cut buffer.  If anything's
 * in the cut buffer, deallocate it first...
 */
Trptr CutBuffer(void)
{
Trptr t, tnext;
Trptr first=NULL, current=NULL;

GLOBALS->shift_click_trace=NULL;		/* so shift-clicking doesn't explode */

t=GLOBALS->traces.first;
while(t)
	{
	if((t->flags)&(TR_HIGHLIGHT)) break;
	t=t->t_next;
	}
if(!t) return(NULL);	/* keeps a double cut from blowing out the buffer */

GLOBALS->signalwindow_width_dirty=1;
GLOBALS->traces.dirty = 1;

FreeCutBuffer();

t=GLOBALS->traces.first;
while(t)
	{
	tnext=t->t_next;
	if(IsSelected(t) || (t->t_grp && IsSelected(t->t_grp)))
	  {
	    /* members of closed groups may not be highlighted */
	    /* so propogate highlighting here */
	    t->flags |= TR_HIGHLIGHT;
	    GLOBALS->traces.bufferlast=t;
	    GLOBALS->traces.buffercount++;

	    /* t->flags&=(~TR_HIGHLIGHT); */
	    RemoveTrace(t, 0);
	    if(!current)
	      {
		first=current=t;
		t->t_prev=NULL;
		t->t_next=NULL;
	      }
	    else
	      {
		current->t_next=t;
		t->t_prev=current;
		current=t;
		t->t_next=NULL;
	      }
	  }
	t=tnext;
	}

return(GLOBALS->traces.buffer=first);
}


/*
 * Delete highlighted traces from the main screen
 * and throw them away.  Do not affect existing cut buffer...
 */
int DeleteBuffer(void)
{
Trptr t, tnext;
Trptr 	 current=NULL;
Trptr    buffer;            /* cut/copy buffer of traces */
Trptr    bufferlast;        /* last element of bufferchain */
int      buffercount;       /* number of traces in buffer */
int      num_deleted;

GLOBALS->shift_click_trace=NULL;		/* so shift-clicking doesn't explode */

t=GLOBALS->traces.first;
while(t)
	{
	if((t->flags)&(TR_HIGHLIGHT)) break;
	t=t->t_next;
	}
if(!t) return(0);	/* nothing to do */

GLOBALS->signalwindow_width_dirty=1;
GLOBALS->traces.dirty = 1;

buffer = GLOBALS->traces.buffer; /* copy cut buffer to make re-entrant */
bufferlast = GLOBALS->traces.bufferlast;
buffercount = GLOBALS->traces.buffercount;

GLOBALS->traces.buffer = NULL;
GLOBALS->traces.bufferlast = NULL;
GLOBALS->traces.buffercount = 0;

t=GLOBALS->traces.first;
while(t)
	{
	tnext=t->t_next;
	if(IsSelected(t) || (t->t_grp && IsSelected(t->t_grp)))
	  {
	    /* members of closed groups may not be highlighted */
	    /* so propogate highlighting here */
	    t->flags |= TR_HIGHLIGHT;
	    GLOBALS->traces.bufferlast=t;
	    GLOBALS->traces.buffercount++;

	    /* t->flags&=(~TR_HIGHLIGHT); */
	    RemoveTrace(t, 0);
	    if(!current)
	      {
		current=t;
		t->t_prev=NULL;
		t->t_next=NULL;
	      }
	    else
	      {
		current->t_next=t;
		t->t_prev=current;
		current=t;
		t->t_next=NULL;
	      }
	  }
	t=tnext;
	}

num_deleted = GLOBALS->traces.buffercount;
FreeCutBuffer();

GLOBALS->traces.buffer = buffer; /* restore cut buffer */
GLOBALS->traces.bufferlast = bufferlast;
GLOBALS->traces.buffercount = buffercount;

return(num_deleted);
}


/*
 * Paste the cut buffer into the main display one and
 * mark the cut buffer empty...
 */
Trptr PasteBuffer(void)
{
  Trptr t, tinsert=NULL, tinsertnext;
  int count;
  Trptr prev;

  if(!GLOBALS->traces.buffer) return(NULL);

  GLOBALS->signalwindow_width_dirty=1;
  GLOBALS->traces.dirty = 1;

  if(!(t=GLOBALS->traces.first))
    {
      t=GLOBALS->traces.last=GLOBALS->traces.first=GLOBALS->traces.buffer;
      prev = NULL;
      while(t)
	{
          t->t_prev = prev; /* defensive re-link move */
	  prev = t;
	  GLOBALS->traces.last=t;
	  GLOBALS->traces.total++;
	  t=t->t_next;
	}

      GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
      GLOBALS->traces.buffercount=0;

      return(GLOBALS->traces.first);
    }

  while(t)
    {
      if(t->flags&TR_HIGHLIGHT)
	{
	  tinsert=t;
	}
      t=t->t_next;
    }


  if(!tinsert) tinsert=GLOBALS->traces.last;

  if(IsGroupBegin(tinsert) && IsClosed(tinsert) && IsCollapsed(tinsert->t_match))
    tinsert=tinsert->t_match;

  tinsertnext=tinsert->t_next;
  tinsert->t_next=GLOBALS->traces.buffer;
  GLOBALS->traces.buffer->t_prev=tinsert;
  GLOBALS->traces.bufferlast->t_next=tinsertnext;
  GLOBALS->traces.total+=GLOBALS->traces.buffercount;

  if(!tinsertnext)
    {
      GLOBALS->traces.last=GLOBALS->traces.bufferlast;
    }
  else
    {
      tinsertnext->t_prev=GLOBALS->traces.bufferlast;
    }

  GLOBALS->traces.scroll_top = GLOBALS->traces.buffer;
  GLOBALS->traces.scroll_bottom = GLOBALS->traces.bufferlast;

  if(GLOBALS->traces.first)
    {
      t = GLOBALS->traces.first;
      t->t_grp = NULL;
      while(t)
	{
	  updateTraceGroup(t);
	  t->flags &= ~TR_HIGHLIGHT;
	  t=t->t_next;
	}
    }

  count = 0;

  if (GLOBALS->traces.buffer)
    {
      t = GLOBALS->traces.buffer;
      while(t)
	{
	  t->flags |= TR_HIGHLIGHT;
	  t=t->t_next;
	  count++;
	  if (count == GLOBALS->traces.buffercount) break;
	}
    }

  /* clean out the buffer */
  GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
  GLOBALS->traces.buffercount=0;

  /* defensive re-link */
  t=GLOBALS->traces.first;
  prev = NULL;
  while(t)
	{
        t->t_prev = prev;
	prev = t;
	t=t->t_next;
	}

  return(GLOBALS->traces.first);
}


/*
 * Prepend the cut buffer into the main display one and
 * mark the cut buffer empty...
 */
Trptr PrependBuffer(void)
{
Trptr t, prev = NULL;
int count;

if(!GLOBALS->traces.buffer) return(NULL);

GLOBALS->signalwindow_width_dirty=1;
GLOBALS->traces.dirty = 1;

t=GLOBALS->traces.buffer;

while(t)
	{
	t->t_prev = prev; /* defensive re-link move */
	prev=t;
	t->flags&=(~TR_HIGHLIGHT);
	GLOBALS->traces.total++;
	t=t->t_next;
	}

if((prev->t_next=GLOBALS->traces.first))
	{
	/* traces.last current value is ok as it stays the same */
	GLOBALS->traces.first->t_prev=prev; /* but we need the reverse link back up */
	}
	else
	{
	GLOBALS->traces.last=prev;
	}

GLOBALS->traces.first=GLOBALS->traces.buffer;

if(GLOBALS->traces.first)
  {
    t = GLOBALS->traces.first;
    t->t_grp = NULL;
    while(t)
      {
	updateTraceGroup(t);
	t->flags &= ~TR_HIGHLIGHT;
	t=t->t_next;
      }
  }

 count = 0;

 if (GLOBALS->traces.buffer)
   {
     t = GLOBALS->traces.buffer;
     while(t)
       {
	 t->flags |= TR_HIGHLIGHT;
	 t=t->t_next;
	 count++;
	 if (count == GLOBALS->traces.buffercount) break;
      }
   }

/* clean out the buffer */
GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
GLOBALS->traces.buffercount=0;

/* defensive re-link */
t=GLOBALS->traces.first;
prev = NULL;
while(t)
	{
        t->t_prev = prev;
	prev = t;
	t=t->t_next;
	}

return(GLOBALS->traces.first);
}


/*
 * avoid sort/rvs manipulations if there are group traces (for now)
 */
static int groupsArePresent(void)
{
Trptr t;
int i, rc = 0;

t=GLOBALS->traces.first;
for(i=0;i<GLOBALS->traces.total;i++)
        {
        if(!t)
                {
                fprintf(stderr, "INTERNAL ERROR: traces.total vs traversal mismatch!  Exiting.\n");
                exit(255);
                }

	if((t->t_grp)||(t->t_match)||(t->flags & TR_GRP_MASK))
		{
		rc = 1; break;
		}

        t=t->t_next;
        }

return(rc);
}


/*************************************************************/


/*
 * sort on tracename pointers (alpha/caseins alpha/sig sort full_reverse)
 */
static int tracenamecompare(const void *s1, const void *s2)
{
char *str1, *str2;

str1=(*((Trptr *)s1))->name;
str2=(*((Trptr *)s2))->name;

if((!str1) || (!*str1))	/* force blank lines to go to bottom */
	{
	if((!str2) || (!*str2))
		{
		return(0);
		}
		else
		{
		return(1);
		}
	}
else
if((!str2) || (!*str2))
	{
	return(-1);		/* str1==str2==zero case is covered above */
	}

return(strcmp(str1, str2));
}


static int traceinamecompare(const void *s1, const void *s2)
{
char *str1, *str2;

str1=(*((Trptr *)s1))->name;
str2=(*((Trptr *)s2))->name;

if((!str1) || (!*str1))	/* force blank lines to go to bottom */
	{
	if((!str2) || (!*str2))
		{
		return(0);
		}
		else
		{
		return(1);
		}
	}
else
if((!str2) || (!*str2))
	{
	return(-1);		/* str1==str2==zero case is covered above */
	}

return(strcasecmp(str1, str2));
}

static int tracesignamecompare(const void *s1, const void *s2)
{
char *str1, *str2;

str1=(*((Trptr *)s1))->name;
str2=(*((Trptr *)s2))->name;

if((!str1) || (!*str1))	/* force blank lines to go to bottom */
	{
	if((!str2) || (!*str2))
		{
		return(0);
		}
		else
		{
		return(1);
		}
	}
else
if((!str2) || (!*str2))
	{
	return(-1);		/* str1==str2==zero case is covered above */
	}

return(sigcmp(str1, str2));
}


/*
 * alphabetization/reordering of traces
 */
int TracesReorder(int mode)
{
Trptr t, prev = NULL;
Trptr *tsort, *tsort_pnt;
#ifdef WAVE_HIERFIX
char *subst, ch;
#endif
int i;
int (*cptr)(const void*, const void*);

if(!GLOBALS->traces.total) return(0);
GLOBALS->traces.dirty = 1;

t=GLOBALS->traces.first;
tsort=tsort_pnt=wave_alloca(sizeof(Trptr)*GLOBALS->traces.total);
memset(tsort_pnt, 0, sizeof(Trptr)*GLOBALS->traces.total);

for(i=0;i<GLOBALS->traces.total;i++)
        {
        if(!t)
                {
                fprintf(stderr, "INTERNAL ERROR: traces.total vs traversal mismatch!  Exiting.\n");
                exit(255);
                }
        *(tsort_pnt++)=t;

#ifdef WAVE_HIERFIX
	if((subst=t->name))
	        while((ch=(*subst)))
        	        {
        	        if(ch==GLOBALS->hier_delimeter) { *subst=VCDNAM_HIERSORT; } /* forces sort at hier boundaries */
        	        subst++;
        	        }
#endif

        t=t->t_next;
        }

switch(mode)
	{
	case TR_SORT_INS:  	cptr=traceinamecompare;   break;
	case TR_SORT_NORM: 	cptr=tracenamecompare;	  break;
	case TR_SORT_LEX:  	cptr=tracesignamecompare; break;
	default: 		cptr=NULL; break;
	}

if((cptr) && (!groupsArePresent()))
	{
	qsort(tsort, GLOBALS->traces.total, sizeof(Trptr), cptr);
	}
	else /* keep groups segregated off on the side and sort names + (indirect pointer to) top-level groups */
	{
	Trptr *tsort_reduced = wave_alloca(sizeof(Trptr)*GLOBALS->traces.total);
	int num_reduced = 0;
	int j;

	memset(tsort_reduced, 0, sizeof(Trptr)*GLOBALS->traces.total);

	for(i=0;i<GLOBALS->traces.total;i++)
	        {
	        if(tsort[i]->flags & TR_GRP_BEGIN)
	                {
	                int cnt = 0;

	                for(j=i;j<GLOBALS->traces.total;j++)
	                        {
	                        if(tsort[j]->flags & TR_GRP_BEGIN) { cnt++; }
	                        else if(tsort[j]->flags & TR_GRP_END) { cnt--; }

	                        if(!cnt)
	                                {
	                                tsort_reduced[num_reduced] = calloc_2(1, sizeof(struct TraceEnt));
	                                tsort_reduced[num_reduced]->name = tsort[i]->name;
	                                tsort_reduced[num_reduced]->is_sort_group = 1;
	                                tsort_reduced[num_reduced]->t_grp = tsort[i];

	                                tsort[j]->t_next = NULL;
	                                num_reduced++;

	                                i = j; break;
	                                }
	                        }
	                }
	                else
	                {
	                tsort_reduced[num_reduced++] = tsort[i];
	                }
	        }

	if(num_reduced)
		{
		if(mode == TR_SORT_RVS) /* reverse of current order */
			{
			for(i=0;i<=(num_reduced/2);i++)
				{
				Trptr t_tmp = tsort_reduced[i];

				j = num_reduced-i-1;
				tsort_reduced[i] = tsort_reduced[j];
				tsort_reduced[j] = t_tmp;
				}
			}
			else
			{
			if(cptr)
				{
				qsort(tsort_reduced, num_reduced, sizeof(Trptr), cptr);
				}
			}
		}

	i = 0;
	for(j=0;j<num_reduced;j++)
		{
		if(!tsort_reduced[j]->is_sort_group)
			{
			tsort[i++] = tsort_reduced[j];
			}
			else
			{
			Trptr trav = tsort_reduced[j]->t_grp;
			free_2(tsort_reduced[j]);
			while(trav)
				{
				tsort[i++] = trav;
				trav = trav->t_next;
				}
			}
		}
	}

tsort_pnt=tsort;
for(i=0;i<GLOBALS->traces.total;i++)
        {
        t=*(tsort_pnt++);

	if(!i)
		{
		GLOBALS->traces.first=t;
		t->t_prev=NULL;
		}
		else
		{
		prev->t_next=t;
		t->t_prev=prev;
		}

	prev=t;

#ifdef WAVE_HIERFIX
	if((subst=t->name))
	        while((ch=(*subst)))
        	        {
        	        if(ch==VCDNAM_HIERSORT) { *subst=GLOBALS->hier_delimeter; } /* restore hier */
        	        subst++;
        	        }
#endif
        }

GLOBALS->traces.last=prev;
if(prev) { prev->t_next=NULL; } /* scan-build */

return(1);
}


Trptr GiveNextTrace(Trptr t)
{
  if(!t) return(t); /* should not happen */

  /* if(t->name) { printf("NEXT: %s %x\n", t->name, t->flags); } */
  UpdateTraceSelection(t);
  if (IsGroupBegin(t) && IsClosed(t))
    {
      Trptr next = t->t_match;
      if (next)
	return (IsCollapsed(next) ? GiveNextTrace(next) : next);
      return NULL;
    }
  else
    {
      Trptr next = t->t_next;
      if (next)
	return (IsCollapsed(next) ? GiveNextTrace(next) : next);
      return NULL;
    }
}

static Trptr GivePrevTraceSkipUpdate(Trptr t, int skip)
{
  if(!t) return(t); /* should not happen */

  /* if(t->name) { printf("PREV: %s\n", t->name); } */
  if(!skip) { UpdateTraceSelection(t); }
  if (IsGroupEnd(t) && IsClosed(t))
    {
      Trptr prev = t->t_match;
      if (prev)
	return (IsCollapsed(prev) ? GivePrevTrace(prev) : prev);
      return NULL;
    }
  else
    {
      Trptr prev = t->t_prev;
      if (prev)
	return (IsCollapsed(prev) ? GivePrevTrace(prev) : prev);
      return NULL;
    }
}

Trptr GivePrevTrace(Trptr t)
{
return(GivePrevTraceSkipUpdate(t, 0));
}


/* propogate selection info down into groups */
void UpdateTraceSelection(Trptr t)
{
  if ((t->t_match) && (IsGroupBegin(t) || IsGroupEnd(t)) && (IsSelected(t) || IsSelected(t->t_match)))
    {
      t->flags          |= TR_HIGHLIGHT;
      t->t_match->flags |= TR_HIGHLIGHT;
    }
  else
  if ((t->t_grp) && IsSelected(t->t_grp))
    {
      t->flags |= TR_HIGHLIGHT;
    }
  else
  if((t->flags & (TR_BLANK|TR_ANALOG_BLANK_STRETCH)) && (GLOBALS->num_ttrans_filters))  /* seek to real xact trace if present... */
        {
	if(!(t->flags & TR_HIGHLIGHT))
		{
	        Trptr tscan = t;
	        int bcnt = 0;
	        while((tscan) && (tscan = tscan->t_prev)) /* && branch formerly was (tscan = GivePrevTraceSkipUpdate(tscan, 1)): overkill as blank traces in transactions don't nest into groups */
	                {
	                if(!(tscan->flags & (TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
	                        {
	                        if(tscan->flags & TR_TTRANSLATED)
	                                {
	                                break; /* found it */
	                                }
	                                else
	                                {
	                                tscan = NULL;
	                                }
	                        }
	                        else
	                        {
	                        bcnt++; /* bcnt is number of blank traces */
	                        }
	                }

	        if((tscan)&&(tscan->vector)&&(IsSelected(tscan)))
	                {
	                bvptr bv = tscan->n.vec;
	                do
	                        {
	                        bv = bv->transaction_chain; /* correlate to blank trace */
	                        } while(bv && (bcnt--));
	                if(bv)
	                        {
				t->flags |= TR_HIGHLIGHT;
	                        }
	                }
		}
        }
}


int UpdateTracesVisible(void)
{
  Trptr t = GLOBALS->traces.first;
  int cnt = 0;

  while(t)
    {
      t = GiveNextTrace(t);
      cnt++;
    }

  GLOBALS->traces.visible = cnt;
  return(cnt);
}

/* where is trace t_in in the list of displayable traces */
int GetTraceNumber(Trptr t_in)
{
  Trptr t = GLOBALS->traces.first;
  int i   = 0;
  int num = -1;

  while(t)
    {
      if (t == t_in)
	{
	  num = i;
	  break;
	}
      i++;
      t = GiveNextTrace(t);
    }

  return(num);
}

unsigned IsShadowed( Trptr t )
{

  if (t->t_grp)
    {
      if (HasWave(t->t_grp))
	{
	  return IsSelected(t->t_grp);
	}
      else
	{
	  return IsShadowed(t->t_grp);
	}
    }

  return 0;
}

char* GetFullName( Trptr t, int *was_packed )
{
  if (HasAlias(t) || !HasWave(t))
    {
      return (t->name_full);
    }
  else if (t->vector)
    {
      return (t->n.vec->bvname);

    }
  else
    {
      return (hier_decompress_flagged(t->n.nd->nname, was_packed));
    }
}


/*
 * sanity checking to make sure there are not any group open/close mismatches
 */
void EnsureGroupsMatch(void)
{
Trptr t = GLOBALS->traces.first;
Trptr last_good = t;
Trptr t2;
int oc_cnt = 0;
int underflow_sticky = 0;
/* Trptr tkill_undeflow = NULL; */ /* scan-build */

while(t)
	{
	if(t->flags & TR_GRP_MASK)
		{
		if(t->flags & TR_GRP_BEGIN)
			{
			oc_cnt++;
			}
		else
		if(t->flags & TR_GRP_END)
			{
			oc_cnt--;
			if(oc_cnt == 0)
				{
				if(!underflow_sticky)
					{
					last_good = t->t_next;
					}
				}
			}

		if(oc_cnt < 0)
			{
			/*
			if(!underflow_sticky)
				{
				tkill_undeflow = t;
				}
			*/ /* scan-build */
			underflow_sticky = 1;
			}
		}
		else
		{
		if((oc_cnt == 0) && (!underflow_sticky))
			{
			last_good = t->t_next;
			}
		}

	t = t->t_next;
	}

if((underflow_sticky) || (oc_cnt > 0))
	{
	t = last_good;
	while(t)
		{
		t2 = t->t_next;
		RemoveTrace(t, 0); /* conservatively don't set "dofree", if there is a reload memory will reclaim */
		t = t2;
		}
	}
}

