/*--------------------------------------------------------------------
 * Name:   mscgen.c
 * 
 * Copyright 1991-2000 Telelogic AB, All rights reserved.
 * 
 * This Program is owned by Telelogic and is protected by national
 * copyright laws and international copyright treaties. Telelogic
 * grants you the right to use this Program on one computer or in one
 * local computer network at any one time.  Under this License you may
 * only modify the source code for the purpose of adapting it to your
 * environment. You must reproduce and include any copyright and
 * trademark notices on all copies of the source code.  You may not
 * use, copy, merge, modify or transfer the Program except as provided
 * in this License.  Telelogic does not warrant that the Program will
 * meet your requirements or that the operation of the Program will be
 * uninterrupted and error free. You are solely responsible that the
 * selection of the Program and the modification of the source code
 * will achieve your intended results and that the results are
 * actually obtained.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types_gen.h"
#include "gci.h"
#include "static.h"
#include "mscgen.h"



#ifdef GENMSC
  const Bool global_variable_genmsc = GcTRUE;
#else
  const Bool global_variable_genmsc = GcFALSE;
#endif

/*--------------------------------------------------------------------
 *
 * Note: There are some comments in the accompanying mscgen.h file,
 * and also in the Telelogic Tau reference manual. This file is
 * subject to change between releases and user changes may not be
 * supported.
 *
 *------------------------------------------------------------------*/


/*--------------------------------------------------------------------
 * Definition Name: MSC_DEFAULT_SYSTEM_NAME
 * Description:
 *
 *  In the composed MSC generation mode, this is used as a default 
 *  name for the IUT. The function MscSetSystemName overrides this
 *  value.
 */

#define MSC_DEFAULT_SYSTEM_NAME "IUT"

/*--------------------------------------------------------------------
 * Definition Name: MSC_DEFAULT_FILE_PREFIX
 * Description:
 *
 *  Used as file name prefix if MscFilePrefix is NULL
 */

#define MSC_DEFAULT_FILE_PREFIX "log_"

/*--------------------------------------------------------------------
 * Definition Name: MSC_MAX_VALUE_LEN
 * Description:
 *
 *  Determines the buffer size for textual encodings of any data
 *  parameters for messages.
 */

#define MSC_MAX_VALUE_LEN	4096

/*--------------------------------------------------------------------
 * Definition Name: MSC_MSCE_MODE, MSC_FILE_MODE
 * Description:
 *
 *  If the MSC_MSCE_MODE flag is set, the tool will attempt to
 *  communicate with the SDT postmaster using the mscpm interface. 
 *  If else, it will attempt to generate new files for each test case.
 *
 *  Note that the two modes are not mutually exclusive.
 */

#ifndef MSC_MSCE_MODE
# undef  MSC_FILE_MODE
# define MSC_FILE_MODE
#endif

/*--------------------------------------------------------------------
 * Definition Name: MscFilePrefix
 * Description:
 *
 *  Static variable possibly holding a prefix for file names with 
 *  possible path that will be pre-pended to any generated files.
 */

#ifdef MSC_FILE_MODE
static char * MscFilePrefix = (char*) 0;
#endif

/*--------------------------------------------------------------------
 * Other inclusions, depending on postmaster library mode
 */

#ifdef MSC_MSCE_MODE
# include <post.h>
# include <sdt.h>
# include "mscpm.h"
#endif

/*--------------------------------------------------------------------
 * Type Name:   MscMessageEvent
 * Description: Represents an MSC Event
 */

typedef struct {
  struct MscMessageEvent * next;/* Next in list */
  unsigned int id;		/* Unsigned int identifier */
  char from[256];		/* From instance name */
  char to[256];			/* To instance name */
  char type[256];		/* Message type name */
  char value[MSC_MAX_VALUE_LEN];/* Encoded representation of value */
} MscMessageEvent;


/*--------------------------------------------------------------------
 * Type Name:   MscDisplayNameEntry
 * Description: Keeping track of instance names to make them unique.
 */

typedef struct _MscDisplayNameEntry 
{
  char name[256];
  int nr;
  char dispname[256];
  struct _MscDisplayNameEntry* next;
} MscDisplayNameEntry;

/*--------------------------------------------------------------------
 * Object Name: dispnamelist
 * Description: List of instance names.
 */
static MscDisplayNameEntry *dispnamelist = NULL;

/*--------------------------------------------------------------------
 * Object Name: MscMessageEventList
 * Description: List of messages sent but still not consumed
 */

static MscMessageEvent * MscMessageEventList = (MscMessageEvent*) 0;

/*--------------------------------------------------------------------
 * Function Names: MscMessageEvent*()
 * Description: Forward definitions of functions manipulating the
 * MscMessageEventList
 */

static const MscMessageEvent *
MscMessageEventEnqueue( const char * from, 
			const char * to, 
			const char * type, 
			const char * value );

static MscMessageEvent *
MscMessageEventFindFirst( const char * from,
			  const char * to );

static void
MscMessageEventRemove( MscMessageEvent * event );

/*--------------------------------------------------------------------
 * Type Name:   MscTimerEvent
 * Description: Used to keep track of running timers
 */

typedef struct {
  struct MscTimerEvent * next;	/* Next in list */
  unsigned int id;		/* Unsigned int identifier */
  char ptc[256];		/* Name of test component */
  char name[256];		/* Timer name */
}  MscTimerEvent;

/*--------------------------------------------------------------------
 * Object Name: MscTimerEventList
 * Description: List of timers that are currently running
 */

static MscTimerEvent * MscTimerEventList = NULL;

/*--------------------------------------------------------------------
 * Function Names: MscTimerEvent*()
 * Description: Forward definitions of functions manipulating the
 * MscMessageEventList
 */

static const MscTimerEvent *
MscTimerEventInsert( const char * ptc, const char * timer );

static MscTimerEvent * 
MscTimerEventFind( const char * ptc, const char * timer );

static void 
MscTimerEventRemove( MscTimerEvent * event );

/*--------------------------------------------------------------------
 * Object Name: mscLogMode
 * Description:
 *  
 *  The mode of logging that will be used. If the mode is 
 *  MscComposedMode a form will be used where the internal behaviour 
 *  of the tester is hidden. If else, it will show internal details, 
 *  such as CP communication and timer activity.
 */

static char * MscSystemName = MSC_DEFAULT_SYSTEM_NAME;

/*--------------------------------------------------------------------
 * Object Name: mscLogMode
 * Description:
 *  
 *  The mode of logging that will be used. If the mode is 
 *  MscComposedMode a form will be used where the internal behaviour 
 *  of the tester is hidden. If else, it will show internal details, 
 *  such as CP communication and timer activity.
 */

static MscLogMode MscCurrentMode = MscDecomposedMode;

/*--------------------------------------------------------------------
 * Object Name: MscEventId;
 * Description:
 *  
 *  Flag determining if a test is in progress. This is reset by the
 *  functions MscInit, MscExit and MscStopTestCase. It is set by the
 *  function MscStartTestCase. It is also used as a counter of event
 *  identifiers, which is used to determine which event belong to a
 *  given action.  
 */

static int MscEventId = 0;

/*--------------------------------------------------------------------
 * Object Name: mscOutputFile;
 * Description:
 *  
 *  Defined if MSC_FILE_MODE is defined at compile time. The current
 *  open output file.
 */

#ifdef MSC_FILE_MODE
static FILE * MscOutputFile = NULL;
#endif

/*--------------------------------------------------------------------
 * Function Name: MscGenerateFileName
 * Description:
 *  
 *  Defined if MSC_FILE_MODE is defined at compile time. Determines a
 *  name of a non-existing log file.
 */

#ifdef MSC_FILE_MODE
static void MscGenerateFileName( const char * testname, char * buffer );
#endif

/*--------------------------------------------------------------------
 * Object Name: mscOutputFile;
 * Description:
 *  
 *  Buffer id of a MSC Editor buffer;
 */

#ifdef MSC_MSCE_MODE
# include "mscpm.h"
#endif

/*--------------------------------------------------------------------
 * Variable Name: MscBufId
 * Description:
 *
 *  Keeps track of the current buffer id of the generated msc in msc 
 *  editor.
 */

#ifdef MSC_MSCE_MODE
static int  MscBufId = 0;
#endif

/*--------------------------------------------------------------------
 * Function Name: MscAppendPREvent;
 * Description:
 *  
 *  Append a PR event to the MSC log(s).
 */

static void MscAppendPREvent( const char * event );

static const char* MscMapDisplayName(const char *name);
static const char* MscGetDisplayName(const char *name);
static MscDisplayNameEntry *MscGetDisplayNameEntry(const char* name);
static MscDisplayNameEntry *MscNewDisplayNameEntry(const char* name, int nr );
static void MscDeleteDisplayNameEntries();

/*--------------------------------------------------------------------
 * Function Name: MscInit
 * Description:
 *
 *   Connect to postmaster if managing a PM connection. Initialize the
 *   static variables.  
 */

void MscInit( )
{
#ifdef GENMSC

  /* Initialize variables */
  MscEventId = 0;		/* Indicates that no test is in progress */
  MscMessageEventList = (MscMessageEvent*) 0;
  MscTimerEventList   = (MscTimerEvent*) 0;

#ifdef MSC_FILE_MODE
  MscOutputFile = NULL;
#endif

#ifdef MSC_MSCE_MODE
  MscBufId = 0;

  /* Initialize postmaster connection if we are to create our own */
  MscPmInit();
#endif

  return;
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscExit
 * Description:
 *
 *   Close files and connections, reset static variables if necessary 
 */

void MscExit( void )
{
#ifdef GENMSC
  MscEventId = 0;
  MscMessageEventList = (MscMessageEvent*) 0;
  MscTimerEventList   = (MscTimerEvent*) 0;

#ifdef MSC_FILE_MODE
  if ( MscOutputFile != NULL ) {
    fclose( MscOutputFile );
    MscOutputFile = NULL;
  }
  MscSetFilePrefix( (const char*) 0 ); /* This is to free memory */
#endif

#ifdef MSC_MSCE_MODE
  MscPmExit( );
#endif
  
  return;
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscSetLogMode
 * Description:
 *
 *  If no test case is in progress, set the current log mode to the
 *  provided new mode.
 */

void MscSetLogMode( MscLogMode logmode )
{
#ifdef GENMSC
  if ( MscEventId == 0 )
    MscCurrentMode = logmode;
  else
    GciLog( GciLogMessage, 
	    "Cannot change log mode while test is running" );
  return;
#else
  VC_NOT_USED(logmode)
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscSetSystemName
 * Description:
 *
 *  If no test case is in progress, set the name of the IUT used by
 *  the composed msc generation mode.
 */

void MscSetSystemName( const char * iut_name )
{
#ifdef GENMSC
  if ( MscEventId == 0 ) {
    /* This will be a memory leak if done over and over: */
    MscSystemName = (char*) VCMALLOC( strlen( iut_name ) + 1 , GcFALSE);
    strcpy( MscSystemName, iut_name );
  }
  else
    GciLog( GciLogMessage, 
	    "Cannot change iut name while test is running" );
  return;
#else
  VC_NOT_USED(iut_name)
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscSetFilePrefix
 * Description:
 *
 *  This function sets a prefix to the file name for generated files.
 *  It has no effect if the current generation mode is MSC_MSCE_MODE.
 *  The function will create a static copy of the filename.
 *
 *  A parameter value of 0 will make the code use the 
 *  MSC_DEFAULT_FILE_PREFIX file prefix as prefix.
 */

void 
MscSetFilePrefix( const char * prefix )
{
#ifdef GENMSC

#ifdef MSC_FILE_MODE 
  if ( MscFilePrefix )
    VCFREE( MscFilePrefix ); 
  if ( prefix ) {
    MscFilePrefix = (char*) VCMALLOC( strlen( prefix ) + 1, GcFALSE );
    strcpy( MscFilePrefix, prefix );
  }
  else
    MscFilePrefix = 0;
#else
  (void) &prefix;		/* Avoid compiler warning */
#endif  
  return;
#else 
  VC_NOT_USED(prefix);
#endif
}


/*--------------------------------------------------------------------
 * Function Name: MscStartTestCase
 * Description:
 *
 *  Create a new MSC for the test case.
 */

void MscStartTestCase( const char * ptc, const char * name )
{
#ifdef GENMSC
  if ( MscEventId != 0 ) {
    GciLog ( GciLogMessage, "MscStartTestCase: Discarding old trace\n" );
    MscStopTestCase( ptc );
  }

  if ( MscCurrentMode == MscNologMode )
    return;

  MscEventId = 1;		/* Will reset event numbering and
				   non-zero value also 
				   indicates that a test is 
				   in progress */

#ifdef MSC_FILE_MODE
  {
    char buffer[1024]; /* may fail if TC name > 1000 characters */
    MscGenerateFileName( name, buffer );
    GciLog( GciLogMessage, "Attempting to create MSC PR trace file," );
    GciLog( GciLogMessage, buffer );

    MscOutputFile = fopen( buffer, "w" );
    if ( MscOutputFile == 0 ) {
      GciLog( GciLogMessage, "Failed to create MSC PR log file" );
    }
  }
#endif

#ifdef MSC_MSCE_MODE
  {
    char buffer[1024]; /* may fail if TC name > 1000 characters */
    int status = 0;
    
    sprintf( buffer, "%d", SET_MSCE );
    MscPmDoRPC( SET_ORGANIZER, SESTART, buffer );
    
    sprintf( buffer, "16 \"%s\" \"\"", name );
    MscPmDoRPC( SET_MSCE, SEMSCECREATEDIAGRAM, buffer );
    sscanf( buffer, "%d %d", &status, &MscBufId );
  }
#endif  

  {
    char buffer[1024];
    int pcoindex;

    sprintf( buffer, "mscdocument %s;", name );
    sprintf( buffer, "msc %s;", name );
    MscAppendPREvent( buffer );

    for ( pcoindex = 0; pcoindex < GciGetNoOfPCOs(); ++pcoindex ) {
	    int id = GciGetPCO( pcoindex );
	    if (IcIsCP( VcContextGetValueByID( id, IcGetActiveContext() ) ) == GcFALSE) {
		    sprintf( buffer, "%s : instance;", GciGetPCOName( id ) );
            MscAppendPREvent( buffer );
	    }
    }

    if ( MscCurrentMode == MscDecomposedMode ) {
      sprintf( buffer, "%s : instance;", ptc );
      MscAppendPREvent( buffer );
    } 

    if ( MscCurrentMode == MscComposedMode ) {
      sprintf( buffer, "%s : instance;", MscSystemName );
      MscAppendPREvent( buffer );
    }
    
  }

  return;
#else
  VC_NOT_USED(name);
  VC_NOT_USED(ptc);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscStopTestCase
 * Description:	  
 * 
 *   Terminate a MSC test case log. Includes generation of some
 *   cleanup code for running timers and the MTC. Also generate the
 *   final verdict as a text box in the MSC.
 */

void MscStopTestCase( const char * ptc )
{
#ifdef GENMSC
  char buffer[4096];

  if ( MscCurrentMode == MscNologMode )
    return;

  /* Generate implicit consumptions of any pending timers or messages
     - the timer list should be empty unless it is a decomposed
     mode. Therefore there is no check. */
  
  while ( MscTimerEventList ) {
    sprintf( buffer, "%s : reset %s,%d comment 'Implicit';",
	     MscTimerEventList->ptc, 
	     MscTimerEventList->name, 
	     MscTimerEventList->id );
    MscAppendPREvent( buffer );
    MscTimerEventRemove( MscTimerEventList );
  }

  while ( MscMessageEventList ) {
    sprintf( buffer, "%s : in %s,%d(%s) from %s comment 'Discarded';",
	     MscMessageEventList->to, 
	     MscMessageEventList->type,
	     MscMessageEventList->id,
	     MscMessageEventList->value,
	     MscMessageEventList->from );
    MscAppendPREvent( buffer );
    MscMessageEventRemove( MscMessageEventList );
  }

  /* Terminate the msc log */
  if ( MscCurrentMode == MscDecomposedMode ) {
    sprintf( buffer, "%s : stop;", ptc );
    MscAppendPREvent( buffer );
  } 

  MscAppendPREvent( "endmsc;" );

#ifdef MSC_FILE_MODE
  if ( MscOutputFile )
    fclose ( MscOutputFile );
  MscOutputFile = NULL;
#endif

  MscDeleteDisplayNameEntries();

  MscEventId = 0;	/* Indicate that the test has stopped */
  return;
#else
  VC_NOT_USED(ptc);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscOut
 * Description:   
 *
 *   Produce an Out event in the MSC log. This corresponds
 *   to an instance sending something. Also store this in
 *   the MessageList for later use with a MscIn event.
 */

void MscOut( MscMessageDirection direction,
	     const char * from, 
	     const char * to, 
	     GciValue * value,
	     const char * comment )
{
#ifdef GENMSC

  char buf[MSC_MAX_VALUE_LEN];
  const MscMessageEvent * event;

  if ( MscCurrentMode == MscNologMode ) 
    return;

  if ( MscCurrentMode == MscComposedMode && direction == MscInternalMessage )
    return;

  MscEncodeValue( value, buf, MSC_MAX_VALUE_LEN );

  /* Possibly adjust the instance names to fit with composed mode */
  if ( MscCurrentMode == MscComposedMode )
    if ( direction == MscSendMessage )
      from = to, to = MscSystemName;
    else
      to = from, from = MscSystemName;

  event = 
    MscMessageEventEnqueue( from, 
			    to, 
			    GciGetTypeNameString( GciGetType( value ) ), 
			    buf );
  
  if ( event ) {
    char buffer[4096]; /* safe size due to event construction */
    
    sprintf( buffer, "%s : out %s,%d%s to %s", 
	     MscMapDisplayName(event->from), event->type, event->id, event->value, 
       MscMapDisplayName(event->to) );
    
    if ( comment && strcmp( comment, "") ) {
      strcat ( buffer, " comment '" );
      strcat ( buffer, comment );
      strcat ( buffer, "';" );
    }
    else
      strcat ( buffer, ";" );
    MscAppendPREvent( buffer );
  }
  else
    GciLog( GciLogMessage, "MscOut: Failed to insert msc event" );

#else
  VC_NOT_USED(direction);
  VC_NOT_USED(from);
  VC_NOT_USED(to);
  VC_NOT_USED(value);
  VC_NOT_USED(comment);
#endif

}

/*--------------------------------------------------------------------
 * Function Name: MscIn
 * Description:   
 *
 *   Produce an In event in the MSC log. This corresponds to an
 *   instance consuming something. The event is to be found in the
 *   MessageList where it was stored by a corresponding MscOut action.
 */

void MscIn( MscMessageDirection direction,
	    const char * from, 
	    const char * to, 
	    const char * comment )
{
#ifdef GENMSC
  MscMessageEvent * event;

  if ( MscCurrentMode == MscNologMode ) 
    return;

  if ( MscCurrentMode == MscComposedMode && direction == MscInternalMessage )
    return;

  /* Possibly adjust the instance names to fit with composed mode */
  if ( MscCurrentMode == MscComposedMode )
    if ( direction == MscSendMessage )
      from = to, to = MscSystemName;
    else
      to = from, from = MscSystemName;

  event = MscMessageEventFindFirst( from, to );
  if ( event ) {
    char buffer[4096]; /* safe size due to event construction */
    sprintf( buffer, "%s : in %s,%d%s from %s", 
	     MscMapDisplayName(event->to), event->type, event->id, event->value, 
       MscMapDisplayName(event->from) );

    if ( comment && strcmp( comment, "") ) {
      strcat ( buffer, " comment '" );
      strcat ( buffer, comment );
      strcat ( buffer, "';" );
    }
    else
      strcat ( buffer, ";" );

    MscAppendPREvent( buffer );
    MscMessageEventRemove( event );
  }
  else {
    GciLog( GciLogMessage, "MscIn: Failed to find message" );
  }
#else
  VC_NOT_USED(direction);
  VC_NOT_USED(from);
  VC_NOT_USED(to);
  VC_NOT_USED(comment);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscImplicitSend
 * Description:
 *
 *  If mode is Decomposed, log the implicit send as a send event
 *  with a comment.
 */

void MscImplicitSend( const char * ptc, GciValue * value )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {    
    char encoded[MSC_MAX_VALUE_LEN];
    char buffer[MSC_MAX_VALUE_LEN+1024];
    MscEncodeValue( value, encoded, MSC_MAX_VALUE_LEN );
    sprintf( buffer, "%s : out %s,%d%s to lost comment 'Implicit Send';",
	     MscMapDisplayName(ptc), GciGetTypeNameString( GciGetType( value ) ), 
       MscEventId++, encoded );
    MscAppendPREvent( buffer );
  }
  return;
#else
  VC_NOT_USED(ptc);
  VC_NOT_USED(value);
#endif

}

/*--------------------------------------------------------------------
 * Function Name: MscStartTimer
 * Description:
 *
 *  If mode is Decomposed, log the starting of the timer in the MSC.
 *  The MscTimerEventInsert function may also produce a cancel event.
 */

void MscStartTimer( const char * ptc, const char * timer, long dur )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {
    const MscTimerEvent * event = MscTimerEventInsert( ptc, timer );
    char * buffer = (char*) VCMALLOC( VCSTRLEN( ptc ) + VCSTRLEN(timer) + 100,GcFALSE );
    sprintf( buffer, "%s : set %s,%ld/* #SDTMSCPAR(%d) */;", 
	     MscMapDisplayName(event->ptc), event->name, event->id, dur );
    MscAppendPREvent( buffer );
    VCFREE( buffer );
  }
  return;
#else
  VC_NOT_USED(ptc);
  VC_NOT_USED(timer);
  VC_NOT_USED(dur);
#endif

}

/*--------------------------------------------------------------------
 * Function Name: MscCancelTimer
 * Description:
 *
 *  Find the event matching the cancel event, and generate a reset
 */

void MscCancelTimer( const char * ptc, const char * timer )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {
    MscTimerEvent * event = MscTimerEventFind( ptc, timer );
    if ( event ) {
      char * buffer = (char*) VCMALLOC( strlen(ptc)+strlen(timer) + 100, GcFALSE );
      sprintf( buffer, "%s : reset %s,%d;", 
	       MscMapDisplayName(event->ptc), event->name, event->id );
      MscAppendPREvent( buffer );
      VCFREE(buffer );
      MscTimerEventRemove( event );
    }
    else
      GciLog( GciLogMessage, "MscCancelTimer: Timer not running" );
  }
  return;
#else
  VC_NOT_USED(ptc);
  VC_NOT_USED(timer);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscTimeoutTimer
 * Description:
 *
 *  Log the named timer as expired
 */

void MscTimeoutTimer( const char * ptc, const char * timer )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {
    MscTimerEvent * event = MscTimerEventFind( ptc, timer );
    if ( event ) {
      char * buffer = (char*) VCMALLOC( strlen(ptc)+strlen(timer) + 100, GcFALSE );
      sprintf( buffer, "%s : timeout %s,%d;", 
	       MscMapDisplayName(event->ptc), event->name, event->id );
      MscAppendPREvent( buffer );
      VCFREE ( buffer );
      MscTimerEventRemove( event );
    }
    else
      GciLog( GciLogMessage, "MscTimeoutTimer: Timer not running" );
  }
  return;
#else
  VC_NOT_USED(ptc);
  VC_NOT_USED(timer);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscCreateComponent
 * Description:
 *  Create the instance axis corresponding to the named component.
 */

void MscCreateComponent( const char * parent, const char * child )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {
    char * buffer = (char*)VCMALLOC( strlen ( parent ) + strlen( child ) + 100, GcFALSE );
    const char* dchild = MscGetDisplayName(child);
    sprintf( buffer, "%s : instance %s;", (dchild != NULL) ? dchild : child, child );
    MscAppendPREvent( buffer );

    sprintf( buffer, "%s : create %s;", parent, (dchild != NULL) ? dchild : child);
    MscAppendPREvent( buffer );
    VCFREE( buffer );
  }
  return;
#else
  VC_NOT_USED(parent);
  VC_NOT_USED(child);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscDoneComponent
 * Description:
 *
 *  Generate a stop msc event to indicate that the particular component
 *  Has terminated. Only applicable in the Decomposed mode.
 */

void MscDoneComponent( const char * ptc )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {
    char * buffer = (char*)VCMALLOC( strlen ( ptc ) + 100, GcFALSE );
    sprintf( buffer, "%s : stop;", MscMapDisplayName(ptc) );
    MscAppendPREvent( buffer );
    VCFREE( buffer );
  }
  return;
#else
  VC_NOT_USED(ptc);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscPreliminaryVerdict
 * Description:
 *  Generate a verdict notification if in DecomposedMode 
 */

void MscPreliminaryVerdict( const char * ptc, const char * verdict )
{
#ifdef GENMSC
  if ( MscCurrentMode == MscDecomposedMode ) {
    /* Set as condition on the current PTC axis */
    char * buffer = (char*) VCMALLOC( VCSTRLEN( ptc ) + 100, GcFALSE );
    sprintf( buffer, "%s : condition %s;", 
	     MscMapDisplayName(ptc), verdict );
    MscAppendPREvent( buffer );
    VCFREE( buffer );
  }

  return;
#else
  VC_NOT_USED(ptc);
  VC_NOT_USED(verdict);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscFinalVerdict
 * Description:
 *   Generate a final verdict notification as a comment in the MSC
 */

void MscFinalVerdict( const char * ptc, const char * verdict )
{
#ifdef GENMSC
  if ( MscCurrentMode != MscNologMode ) {
    char * buffer = (char*) VCMALLOC( strlen( MscMapDisplayName(ptc) ) + 100, GcFALSE );
    sprintf( buffer, "text 'Result: %s';", verdict );
    MscAppendPREvent( buffer );
    
    VCFREE( buffer );  
  }
  return;
#else
  VC_NOT_USED(ptc);
  VC_NOT_USED(verdict);
#endif
}


/*--------------------------------------------------------------------
 * Function Name: MscGenerateFileName
 * Description:
 *  Generate a file name for an MSC PR file that does not already 
 *  exist. An improvement may be to add a directory path in front
 *  of the name...
 */

#ifdef MSC_FILE_MODE
static void MscGenerateFileName( const char * testname, char * buffer )
{
  FILE * file = NULL;
  int try = 1;
  while ( try <= 9999 /* sanity check */ ) {
    const char * prefix = MSC_DEFAULT_FILE_PREFIX;
    if ( MscFilePrefix )
      prefix = MscFilePrefix;
    sprintf( buffer, "%s%s_%04d.mpr", prefix, testname, try++ );
    file = fopen( buffer, "r" );
    if ( file == NULL )
      break;
    fclose( file );
  } 
  return;
}
#endif



static void MscAppendPREvent( const char * event )
{
#ifdef MSC_FILE_MODE
  if ( MscOutputFile != 0 ) {
    fprintf( MscOutputFile, "%s\n", event );
    fflush( MscOutputFile );
  }
#endif

#ifdef MSC_MSCE_MODE
  {
    char msg[8192];
    int status, objectId = 0;
    sprintf( msg, "%d 0 \"%s\"", MscBufId, event );
    MscPmDoRPC( SET_MSCE, SEINSERTOBJECT, msg );
    sscanf( msg, "%d %d", &status, &objectId );
    sprintf( msg, "%d %d", MscBufId, objectId );
    MscPmDoRPC( SET_MSCE, SESHOWOBJECT, msg );
    /* ignore reply */
  }
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscMessageEventEnqueue()
 * Description: Allocate and put a message event at the front of the 
 * list of currently pending messages.
 */

static const MscMessageEvent *
MscMessageEventEnqueue( const char * from, 
			const char * to, 
			const char * type, 
			const char * value )
{
  MscMessageEvent * event = 
    (MscMessageEvent*) VCMALLOC(sizeof( MscMessageEvent ) , GcFALSE);

  /* Assign the various fields of the structure, truncate any
     fields that are to long for the structures field sizes */
  
  event->id = (unsigned int)MscEventId++;
  
  strncpy( event->from, from, sizeof( event->from ));
  event->from[sizeof( event->from ) - 1 ] = '\0';
  
  strncpy( event->to, to, sizeof( event->to ));
  event->to[sizeof( event->to ) - 1 ] = '\0';
  
  strncpy( event->type, type, sizeof( event->type ));
  event->type[sizeof( event->type ) - 1 ] = '\0';
  
  strncpy( event->value, value, sizeof( event->value ));
  event->value[sizeof( event->value ) - 1 ] = '\0';
  
  event->next = (struct MscMessageEvent*) MscMessageEventList;
  MscMessageEventList = event;
  
  return event;
}

/*--------------------------------------------------------------------
 * Function Name: MscMessageEventFindFirst()
 * Description: 
 * 
 *  Search for the least recently inserted event that matches the from
 *  and to criteria. Relies on that new events are inserted first in
 *  the list.
 */

static MscMessageEvent *
MscMessageEventFindFirst( const char * from,
			  const char * to )
{
#ifdef GENMSC
  MscMessageEvent * last = (MscMessageEvent*) 0;
  MscMessageEvent * event;
  for ( event = (MscMessageEvent*) MscMessageEventList; 
	event; event = (MscMessageEvent*) event->next )
    if ( !strcmp( from, event->from ) && !strcmp( to, event->to ) )
      last = event;

  if ( !last )
    GciLog( GciLogMessage, "MscMessageEventFindFirst: Failed to find event" );
  
  return last;
#else
  VC_NOT_USED(from);
  VC_NOT_USED(to);
  return NULL;
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscMessageEventRemove()
 * Description: 
 * 
 *  Remove the event pointed to by the event pointer from the
 *  list. Free memory associated with it.
 */

static void
MscMessageEventRemove( MscMessageEvent * event )
{
#ifdef GENMSC
  MscMessageEvent * prev = (MscMessageEvent*) event;
  int found_in_list = 0;
  if ( event ) {
    if ( MscMessageEventList == event ) {
      MscMessageEventList = (void*) event->next;
      found_in_list = 1;
    }
    else for ( prev = (void*) MscMessageEventList; 
	       prev; prev = (void*) prev->next )
      if ( prev->next == (void*) event ) {
	prev->next= ((MscMessageEvent*)prev->next)->next;
	found_in_list = 1;
	break;
      }    

    if ( !found_in_list )
      GciLog (GciLogMessage, "MscMessageEventRemove: Removing event not in event list" );
    
    /* Always free the memory of the event object */
    VCFREE( event );	  
  } 
  else
    GciLog(GciLogMessage, "MscMessageEventRemove: Attempting to remove NULL event" );
#else
  VC_NOT_USED(event);
#endif
}


/*--------------------------------------------------------------------
 * Function Name: MscTimerEventInsert()
 * Description: 
 * 
 *  If the timer event does not exist in the list, allocate it and 
 *  insert it in the list. If it does exist, generate an implicit reset
 *  and then update the event id (to save a free/malloc). 
 */

static const MscTimerEvent *
MscTimerEventInsert( const char * ptc, const char * timer )
{
  MscTimerEvent * event = (MscTimerEvent*) MscTimerEventFind( ptc, timer );
  if ( event ) {
    /* Implicit reset could be generated */
    /* 
       char * buffer = 
       (char*) malloc( strlen(event->ptc) + strlen(event->name) + 100 );
       sprintf( buffer, 
                "%s : reset %s,%d comment 'Restarting';",
                event->ptc, event->name, event->id );
       MscAppendPREvent( buffer );
       free ((void*) buffer );
    */
  }
  else {
    event = (MscTimerEvent* ) VCMALLOC( sizeof (MscTimerEvent), GcFALSE );
    strncpy( event->ptc, ptc, sizeof( event->ptc ) );
    event->ptc[sizeof( event->ptc ) - 1 ] = '\0'; 
    
    strncpy( event->name, timer, sizeof( event->name ) );
    event->name[sizeof( event->name ) - 1 ] = '\0'; 
    
    event->next = (struct MscTimerEvent*) MscTimerEventList;
    MscTimerEventList = event;
  }
  
  event->id = (unsigned int)MscEventId++;
  
  return event;
}

/*--------------------------------------------------------------------
 * Function Name: MscMessageEventFind()
 * Description: 
 * 
 *  Find a timer event associated with the ptc and the timer
 */

static MscTimerEvent * 
MscTimerEventFind( const char * ptc, const char * timer )
{
  MscTimerEvent * event;
  for ( event = (MscTimerEvent*) MscTimerEventList; 
	event; event = (MscTimerEvent*) event->next )
    if ( !strcmp( ptc, event->ptc ) && !strcmp( timer, event->name ) )
      return event;
      
  return event;
}

/*--------------------------------------------------------------------
 * Function Name: MscTimerEventRemove()
 * Description: 
 * 
 *  Remove the event pointed to by the event pointer from the
 *  list. Free memory associated with it.
 */

static void 
MscTimerEventRemove( MscTimerEvent * event )
{
#ifdef GENMSC
  MscTimerEvent * prev = (MscTimerEvent*) event;
  int found_in_list = 0;
  if ( event ) {
    if ( MscTimerEventList == (void*) event ) {
      MscTimerEventList = (MscTimerEvent*) event->next;
      found_in_list = 1;
    }
    else for ( prev = (MscTimerEvent*) MscTimerEventList; 
	       prev; prev = (MscTimerEvent*) prev->next )
      if ( prev->next == (void*) event ) {
	prev->next= ((MscTimerEvent*)prev->next)->next;
	found_in_list = 1;
	break;
      }    

    if ( !found_in_list )
      GciLog (GciLogMessage, "MscTimerEventRemove: Removing event not in event list" );
    
    /* Always free the memory of the event object */
    VCFREE( event );	  
  } 
  else
    GciLog(GciLogMessage, "MscTimerEventRemove: Attempting to remove NULL event" );
#else
  VC_NOT_USED(event);
#endif
}

/*--------------------------------------------------------------------
 * Function Name: MscEncodeValue
 * Description: 
 * 
 *  Store a textual representation of the value in memory at pos,
 *  provided that there are enough bytes left. The representation is
 *  similar to the simulator representation with some exceptions for
 *  the SET, CHOICE, HEXSTRING and ENUMERATED types that are simpler
 *  or not supported.
 *
 *  Note that the recursive call in composite types reserves some
 *  characters such that it should be able to at least complete the
 *  current call.
 */

void MscEncodeValue ( GciValue * value, char * pos, int left )
{
#ifdef GENMSC
  int i = 0;

  /* Can be defined to disable value logs and get a more high-level
     format: */
  unsigned int index = 0;
  *pos = '\0';
  
#ifndef MSC_NO_VALUE_LOGGING
  
  if (value == NULL) {
    if ( left > 1 )
      strcpy( pos, "-" );
  } 
#ifdef MSC_NO_OMIT_LOGGING
  else if (GciGetOMIT(value) == GcTRUE) {
    strcpy(pos, "");
  } 
#endif
  else {
    switch ( GciGetBaseType( value ) ) {
      /* Handling of composite types */
    case GcSEQUENCED:
    case GcSETD:
    case GcSEQUENCEOFD:
    case GcSETOFD:
      if ( left > 3 )
	VCSTRCAT( pos, "( " ), left -= strlen( pos ), pos  += VCSTRLEN( pos );
      for ( index = 0 ; index < (unsigned int)GciSeqSize( value ); ++index ) {
	GciValue* svalue = NULL;
	if ( left < 4 ) 
	  break;
	svalue = GciGetField( value, index + 1 );
	if (GciGetOMIT(svalue))
	  continue;
	MscEncodeValue( svalue, pos, left - 4 );
	left -= strlen( pos ); pos  += strlen( pos );
	if ( left > 3 && index + 1 < (unsigned int)GciSeqSize( value ) ) {
	  strcat(pos, ", ");
	  left -= strlen( pos ); pos  += strlen( pos );
	}
      }
      if ( left > 3 )
	strcat( pos, ")" );
      break;
      
      /* Handling of simple types */
    case GcBOOLEAND:
      if ( left > 10 )
	sprintf( pos, "%s", GciGetBOOLEAN( value ) ? "true" : "false" );
      break;
      
    case GcENUMERATEDD:
      if ( left > (int)strlen( EGciGetENUMERATEDName( value ) ) + 10 )
    sprintf( pos, "%s", EGciGetENUMERATEDName( value ) );
      break;

    case GcINTEGERD:
      if ( left > 10 )
	sprintf( pos, "%ld", GciGetINTEGER( value ) );
      break;
      
    case GcBITSTRINGD:
      if ( left > (int)strlen( GciGetBIT_STRING( value ) ) + 10 )
	sprintf( pos, "'%s'B", GciGetBIT_STRING( value ) );
      break;
      
    case GcOCTETSTRINGD:
      if ( left > (int)strlen( GciGetOCTET_STRING( value ) ) + 10 )
	sprintf( pos, "'%s'H", GciGetOCTET_STRING( value ) );
      break;
      
    case GcHEXSTRINGD:
      if ( left > (int)strlen( GciGetHEXSTRING( value ) ) + 10 )
	sprintf( pos, "'%s'H", GciGetHEXSTRING( value ) );
      break;
      
    case GcVIDEOTEXSTRINGD:
    case GcGENERALSTRINGD:
    case GcGRAPHICSTRINGD:
    case GcISO646STRINGD:
    case GcT61STRINGD:
    case GcTELETEXSTRINGD:
    case GcNUMERICSTRINGD:
    case GcPRINTABLESTRINGD:
    case GcVISIBLESTRINGD:
    case GcIA5STRINGD:
    if ( left > (int)strlen( GciGetIA5String( value ) ) + 10 )
      sprintf( pos, "'%s'", GciGetIA5String( value ) );
    break;
    case GcNULLD:
      if(left > 5  + 10)
        sprintf(pos, "'NULL'");
      break;
    
    case GcCHOICED:
      MscEncodeValue(GciGetCHOICE(value), pos, left);
      break;
   
      
    case GcOBJECTIDENTIFIERD:
      if (left > GciOBJECT_IDENTIFIERSize(value)*10)
        for (i = 0; i < GciOBJECT_IDENTIFIERSize(value); i++) {
          sprintf(pos, "'%d'", GciGetOBJECT_IDENTIFIERComponent(value, i));
        }
      break;

    default:
      GciLog( GciLogMessage, "MscEncodeValue: type not implemented:" );
      GciLog( GciLogMessage, GciGetTypeNameString( GciGetType( value ) )); 
    }
  } 
  left -= strlen( pos ), pos  += strlen( pos );
  
  if ( left < 1 )
    GciLog( GciLogMessage, "MscEncodeValue: Too small buffer" );
  
#endif /* MSC_NO_VALUE_LOGGING */
  return;
#else
  VC_NOT_USED(pos);
  VC_NOT_USED(value);
  VC_NOT_USED(left);

#endif
}


static const char* 
MscMapDisplayName(const char *name)
{
  MscDisplayNameEntry *ptr = MscGetDisplayNameEntry(name);

  if (ptr == NULL)
    return (const char*)name;
  else
    return (const char*)ptr->dispname;
}

static const char* 
MscGetDisplayName(const char *name)
{
  MscDisplayNameEntry *ptr = MscGetDisplayNameEntry(name);

  if (ptr == NULL) {
    /* Allocate new entry */
    MscDisplayNameEntry *elem = MscNewDisplayNameEntry(name, 0);

    if (elem == NULL)
      return name;
    
    elem->next = dispnamelist;
    dispnamelist = elem;

    return (const char*)elem->dispname;
  } else {
    (ptr->nr)++;
    sprintf(ptr->dispname, "%s_%d", name, ptr->nr);
    
    return (const char *)ptr->dispname;
  }
}

static MscDisplayNameEntry *
MscGetDisplayNameEntry(const char* name)
{
  MscDisplayNameEntry *ptr = dispnamelist;

  while (ptr != NULL) {
    if (strcmp(name, ptr->name) == 0)
      return ptr;
    ptr = ptr->next;
  }

  return NULL;
}

static MscDisplayNameEntry *
MscNewDisplayNameEntry(const char* name, int nr )
{
  MscDisplayNameEntry *elem = (MscDisplayNameEntry *)malloc(sizeof(MscDisplayNameEntry));
  if (elem == NULL)
    return NULL;

  elem->nr = nr;
  strcpy(elem->name, name);

  if (nr == 0)
    strcpy(elem->dispname, elem->name);
  else
    sprintf(elem->dispname, "%s_%d", elem->name, nr);

  elem->next = NULL;

  return elem;
}


static void 
MscDeleteDisplayNameEntries()
{
  MscDisplayNameEntry *ptr = dispnamelist;

  while (ptr != NULL) {
    dispnamelist = ptr->next;
  
    free(ptr);
    ptr = dispnamelist;
  }
}
