//  -*- C++ -*- <this line is for emacs to recognize it as C++ code>
/*****************************************************************************
  The following code is derived, directly or indirectly, from the SystemC
  source code Copyright (c) 1996-2002 by all Contributors.
  All Rights reserved.

  The contents of this file are subject to the restrictions and limitations
  set forth in the SystemC Open Source License Version 2.3 (the "License");
  You may not use this file except in compliance with such restrictions and
  limitations. You may obtain instructions on how to receive a copy of the
  License at http://www.systemc.org/. Software distributed by Contributors
  under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
  ANY KIND, either express or implied. See the License for the specific
  language governing rights and limitations under the License.

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

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

  scv_debug.cpp -- A trace/debug facility for the SystemC Verification
  Standard library

  Original Authors (Cadence Design Systems, Inc):
  Norris Ip, Dean Shea, John Rose, Jasvinder Singh, William Paulsen,
  John Pierce, Rachida Kebichi, Ted Elkind, David Bailey
  2002-09-23

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

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

  MODIFICATION LOG - modifiers, enter your name, affiliation, date and
  changes you are making here.

      Name, Affiliation, Date:
  Description of Modification:

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

//
// Generated by configure
//
#include "scv/scv_config.h"

#include <time.h>
#include <string.h>

#include "scv/scv_util.h"
#include "scv/scv_debug.h"
#include "scv/scv_constraint.h"
#include "scv/scv_report.h"
#include "scv/scv_tr.h"

#define SCV_DEBUG_DEFAULT_TRACE_FILE "scv_debug.log"

#ifdef _MSC_VER
#define strcasecmp stricmp
#else
extern "C" int strcasecmp(const char *, const char *);
#endif

int scv_debug::_debug = -1;
scv_debug *scv_debug::scv_debug_p = 0;
int scv_debug::local_origination = false;

// Add your facility name here:
const char *scv_debug::facility_names[] = {
  "all",
  "data_structures",
  "introspection",
  "messages",   // message handling
  "randomization",
  "recording",  // automatic transaction recording (mainly tbvFiberT)
  "signals",
  "transactors",
  "last"        // Add a new one above this
};

const char *scv_debug::_kind = "scv_debug";

// Prototypes for local functions
static bool valid_facility(scv_debug::debug_facilities facility);
static bool valid_level(int level);


scv_debug::scv_debug(const char *filename)
{
  int i;
  char localString1[1000];
  time_t t;

  if (filename == 0) {
    file_p = fopen(SCV_DEBUG_DEFAULT_TRACE_FILE, "w");
  } else {
    file_p = fopen(filename, "w");
  }

  send_to_stdout = 1;
  for (i = 0; i < LAST; i++) {
	facility_levels[i] = INITIAL_DEBUG_LEVEL;
	if (i != ALL) {
	  set_level_for_classes(i, INITIAL_DEBUG_LEVEL);
	}
  }

  t = time(0);
  sprintf(localString1, "SCV Trace started at %s\n",
    ctime(&t));

  send_to_log(localString1);
}

scv_debug::~scv_debug()
{ close_trace_file(); }

void scv_debug::record_data(
  const char *filename,
  int lineno,
  scv_object_if *object_p,
  int target_level,
  const char *data
)
{
  char localString[1000];

  if (scv_debug_p == 0) return;
 
  if (target_level == 0) {
    sprintf(
      localString,
      "scv_debug: Class \"%s\", in File \"%s\", Line %d:\n",
      object_p->kind(), filename, lineno
    );
  }
  else {
    sprintf(
      localString,
      "scv_debug: Class \"%s\", Level %d, in File \"%s\", Line %d:\n",
      object_p->kind(), target_level, filename, lineno
    );
  }
  scv_debug_p->send_to_log(localString);

  sprintf(localString, "  \"%s\"\n", data);
  scv_debug_p->send_to_log(localString);
}

int scv_debug::check(int actual_level, int target_level)
{
  if (target_level <= SUSPENDED_DEBUG_LEVEL) return 0;
  if (actual_level <= SUSPENDED_DEBUG_LEVEL) return 0;
  if (actual_level == 0) return 1;
  return actual_level >= target_level;
}

void scv_debug::send_to_log(const char *the_string)
{
  if (file_p != 0) fprintf(file_p, "%s", the_string);
  if (send_to_stdout == 1) scv_out << the_string;
}

void scv_debug::open_trace_file(const char *filename)
{

  if (scv_debug_p == 0) {
    scv_debug_p = new scv_debug(filename);
  }
  else {
    scv_debug::close_trace_file();
    scv_debug_p->file_p = fopen(filename, "w");
  }
}

void scv_debug::close_trace_file()
{
  time_t t;
  char localString[1000];

  if (scv_debug_p == 0) return;
  if (scv_debug_p->file_p == 0) return;

  t = time(0);
  sprintf(localString, "\n*** Closing SCV Trace file at %s\n",
    ctime(&t));
  scv_debug_p->send_to_log(localString);

  fclose(scv_debug_p->file_p);
  scv_debug_p->file_p = 0;
}

// The level setting routines of the facilities, such as set_debug(),
// should call scv_debug::set_facility_level() to set
// the facility level locally so that it may be saved away.  We need to
// have a saved copy of the level setting so that when facilities are 
// suspended the level setting is available when the facility is resumed.
//
void scv_debug::set_level_for_classes(int facility, int level)
{
  local_origination = true;
  switch ( facility ) {
    case scv_debug::DATA_STRUCTURES :
         _scv_data_structure::set_debug(level);
	 break;
    case scv_debug::INTROSPECTION :
	 scv_extensions_if::set_debug(level);
	 break;
    case scv_debug::MESSAGES :
#	 ifndef _SCV_USE_SC_REPORT
	   scv_report_handler::set_debug(level);
#	 endif
	 break;
    case scv_debug::RANDOMIZATION :
         scv_random::set_debug(level);
         scv_expression::set_debug(level);
         scv_constraint_base::set_debug(level);
	 break;
    case scv_debug::RECORDING :
         scv_tr_db::set_debug(level);
         scv_tr_stream::set_debug(level);
         scv_tr_handle::set_debug(level);
         scv_tr_generator_base::set_debug(level);
	 break;
    case scv_debug::SIGNALS :
	 break;
    case scv_debug::TRANSACTORS :
	 break;
    default :
	 assert(0);
	 break;
  }
  local_origination = false;
}

void scv_debug::suspend(int facility)
{
  char localString[1000];

  if (scv_debug_p == 0) return;

  if (facility == 0) {
    scv_debug_p->send_to_log("*** All tracing is now suspended.\n");
    for (int i = 1; i < LAST; i++) set_level_for_classes(i,SUSPENDED_DEBUG_LEVEL);
  }
  else {
    sprintf(localString, "*** All tracing for facility %s is suspended.\n",
      scv_debug_p->facility_names[facility]);
    scv_debug_p->send_to_log(localString);
    set_level_for_classes(facility,SUSPENDED_DEBUG_LEVEL);
  }
}

void scv_debug::resume(int facility)
{
  char localString[1000];

  if (scv_debug_p == 0) scv_debug_p = new scv_debug();

  if (facility == 0) {
    scv_debug_p->send_to_log("*** All tracing is now resumed.\n");
    for (int i = 1; i < LAST; i++) set_level_for_classes(i,scv_debug_p->facility_levels[i]);
  }
  else {
    sprintf(localString, "*** All tracing for facility %s is resumed.\n",
      scv_debug_p->facility_names[facility]);
    scv_debug_p->send_to_log(localString);
    set_level_for_classes(facility,scv_debug_p->facility_levels[facility]);
  }
}

void scv_debug::set_level(debug_facilities facility, int level)
{
  char localString[1000];

  if (scv_debug_p == 0) scv_debug_p = new scv_debug();

  if ( facility == ALL ) {
    for ( int i = 1; i < LAST; i++ ) {
      scv_debug_p->facility_levels[i] = level;
      set_level_for_classes(i,level);
    }
    sprintf(
      localString, "*** All tracing at level %d is now set.\n", level
    );
  }
  else {
    scv_debug_p->facility_levels[facility] = level;
    set_level_for_classes(facility,level);
    sprintf(
      localString, "*** Tracing for facility %s at level %d is now set.\n",
      scv_debug_p->facility_names[facility], level
    );
  }
  scv_debug_p->send_to_log(localString);
}

void scv_debug::set_level(const char *facility_name, int level)
{
  if (scv_debug_p == 0) scv_debug_p = new scv_debug();

  for (int i = 0; i < LAST; i++) {
    if (strcasecmp(facility_name, scv_debug_p->facility_names[i]) == 0) {
      scv_debug::set_level((debug_facilities) i, level);
      return;
    }
  }
}

// Set tracing level routine for facilities to call
void scv_debug::set_facility_level(debug_facilities facility, int level)
{
  char localString[1000];

  if (local_origination) return;  // Setting of the level was initiated from
                                  // scv_debug, facility_level is already updated.
  if (scv_debug_p == 0) scv_debug_p = new scv_debug();

  if (valid_facility(facility)) {
    if (valid_level(level)) {
      if (facility == ALL) {
	for (int i = 1; i < LAST; i++) scv_debug_p->facility_levels[i] = level;
	sprintf(localString, "*** All tracing at level %d is now set.\n", level);
      }
      else {
	scv_debug_p->facility_levels[facility] = level;
	sprintf(localString, "*** Tracing for facility %s at level %d is now set.\n",
	scv_debug_p->facility_names[facility], level);
      }
    }
    else {
      sprintf(
	localString, "*** Attempt to set invalid tracing level %d for facility %s.\n",
	level, scv_debug_p->facility_names[facility]
      );
    }
  }
  else { 
    sprintf(localString, "*** Invalid tracing facility: %d.\n", facility);
  }
  scv_debug_p->send_to_log(localString);
  return;
}

void scv_debug::open_stdout()
{
  if (scv_debug_p == 0) scv_debug_p = new scv_debug();
  scv_debug_p->send_to_stdout = 1;
}

void scv_debug::close_stdout()
{
  if (scv_debug_p == 0) scv_debug_p = new scv_debug();
  scv_debug_p->send_to_stdout = 0;
}

void scv_debug::set_debug(int debug)
{
  if ( _debug == debug ) return;
  _debug = debug;
  scv_debug::set_facility_level(scv_debug::MESSAGES,debug);
}

void scv_debug::indent(ostream& os, int indent)
{ for ( int i = 0; i < indent; i++ ) os << " "; }

void scv_debug::print(ostream&, int, int ) const {}

static bool valid_facility(scv_debug::debug_facilities facility)
{
  if (facility >= scv_debug::ALL && facility < scv_debug::LAST) {
    return true;
  }
  else {
    return false;
  }
}

#ifndef _HPUX_SOURCE
#define MIN(A,B) ( (A>B) ? B : A )
#endif

static bool valid_level(int level)
{
  if ( level >= MIN(scv_debug::SUSPENDED_DEBUG_LEVEL,scv_debug::INITIAL_DEBUG_LEVEL) ) {
    return true;
  }
  else {
    return false;
  }
}
