#ifndef morpheo_behavioural_Signal_h
#define morpheo_behavioural_Signal_h

/*
 * $Id$
 *
 * [ Description ]
 * 
 */

#ifdef SYSTEMC
#include "systemc.h"
#endif

#include <stdint.h>
#include <iostream>

#ifdef VHDL
#include "Behavioural/include/Vhdl.h"
#endif

#include "Behavioural/include/Direction.h"
#include "Behavioural/include/XML.h"
#include "Common/include/ErrorMorpheo.h"
#include "Common/include/ToBase2.h"
#include "Common/include/ToString.h"
#include "Common/include/Debug.h"

using namespace std;

namespace morpheo              {
namespace behavioural          {

  typedef enum {UNKNOW                     ,
		BOOL                       ,
		UINT8_T                    ,
		UINT16_T                   ,
		UINT32_T                   ,
		UINT64_T                   } type_info_t;

  typedef enum {PORT_VHDL_YES_TESTBENCH_YES,
		PORT_VHDL_YES_TESTBENCH_NO ,
		PORT_VHDL_NO_TESTBENCH_YES ,
		PORT_VHDL_NO_TESTBENCH_NO  ,
		CLOCK_VHDL_YES             ,
		CLOCK_VHDL_NO              ,
		RESET_VHDL_YES             ,
		RESET_VHDL_NO              } presence_port_t;

  class Signal
  {
    friend class Interface;

    // -----[ fields ]----------------------------------------------------
  private   : const string          _name         ;
  private   : const direction_t     _direction    ;
  private   : const uint32_t        _size         ;
  private   : const presence_port_t _presence_port;

  private   : Signal *              _connect_to_signal;   // the actual implementaion, this signal link with one signal (but if signal is an output, it can be connect with many signal ...)
  private   : Signal *              _connect_from_signal; // producter of signal. If NULL, then producteur is the current entity
  private   : bool                  _is_allocate  ; // Have allocate a sc_in or sc_out port
  private   : void *                _sc_signal    ; // sc_in or sc_out associated at this signal
  private   : bool                  _is_map       ; 
  private   : void *                _sc_signal_map; // sc_out generated this signal
  private   : type_info_t           _type_info    ;

#ifdef VHDL_TESTBENCH
  private   : list<string>        * _list_value   ;
#endif

    // -----[ methods ]---------------------------------------------------
  public    :                   Signal          (string          name          ,
						 direction_t     direction     ,
						 uint32_t        size          ,
						 presence_port_t presence_port = PORT_VHDL_YES_TESTBENCH_YES);
  public    :                   Signal          (const Signal &);
  public    :                   ~Signal         ();

  public    : string            get_name        (void);
  public    : uint32_t          get_size        (void);
  public    : bool              get_is_map      (void);
  public    : Signal *          get_connect_to_signal (void);
  public    : Signal *          get_connect_from_signal (void);
  public    : direction_t       get_direction   (void);

  public    : bool              presence_vhdl      (void);
  public    : bool              presence_testbench (void);

//   public    : void              mapping         (Signal * signal);
  public    : void              link            (Signal * signal  ,
						 bool     is_port_component);

#ifdef SYSTEMC         
  public    :template <typename T>
             T                  read (void)
    {
      switch (_direction)
	{
	case IN  : {return read_in  <T>();}
	case OUT : {return read_out <T>();}
	default  : throw (ErrorMorpheo ("Signal \""+_name+"\" : direction unknow."));
	}
    }

  public    :template <typename T>
             T                  read_in (void)
    {
      switch (_type_info)
	{
	case BOOL     : return (static_cast<sc_in  <bool    > *>(_sc_signal_map)->read()); 
	case UINT8_T  : return (static_cast<sc_in  <uint8_t > *>(_sc_signal_map)->read()); 
	case UINT16_T : return (static_cast<sc_in  <uint16_t> *>(_sc_signal_map)->read());
	case UINT32_T : return (static_cast<sc_in  <uint32_t> *>(_sc_signal_map)->read()); 
	case UINT64_T : return (static_cast<sc_in  <uint64_t> *>(_sc_signal_map)->read()); 
	default       : throw (ErrorMorpheo ("Signal \""+_name+"\" : type unknow."));
	}
    }

  public    :template <typename T>
             T                  read_out(void)
    {
      switch (_type_info)
	{
	case BOOL     : return (static_cast<sc_out <bool    > *>(_sc_signal_map)->read()); 
	case UINT8_T  : return (static_cast<sc_out <uint8_t > *>(_sc_signal_map)->read()); 
	case UINT16_T : return (static_cast<sc_out <uint16_t> *>(_sc_signal_map)->read());
	case UINT32_T : return (static_cast<sc_out <uint32_t> *>(_sc_signal_map)->read()); 
	case UINT64_T : return (static_cast<sc_out <uint64_t> *>(_sc_signal_map)->read()); 
	default       : throw (ErrorMorpheo ("Signal \""+_name+"\" : type unknow."));
	}
    }

#undef  FUNCTION
#define FUNCTION "Signal::alloc"
  public    : template <typename T>
              void              alloc           (void * sc_signal)
    {
      log_printf(FUNC,Behavioural,FUNCTION,"Begin");

      if (_type_info != UNKNOW)
	throw (ErrorMorpheo ("Signal \""+_name+"\" : already allocate."));

      _is_allocate    = true;
      _sc_signal      = sc_signal;
      _sc_signal_map  = sc_signal;

      log_printf(TRACE,Behavioural,FUNCTION, "Allocation of %s - %.8x", _name.c_str(), (uint32_t)(_sc_signal_map));

      if (typeid(T) == typeid(bool    ))
	_type_info = BOOL;
      else
      if (typeid(T) == typeid(uint8_t ))
	_type_info = UINT8_T;
      else
      if (typeid(T) == typeid(uint16_t))
	_type_info = UINT16_T;
      else
      if (typeid(T) == typeid(uint32_t))
	_type_info = UINT32_T;
      else
      if (typeid(T) == typeid(uint64_t))
	_type_info = UINT64_T;
      else
	_type_info = UNKNOW;

      log_printf(FUNC,Behavioural,FUNCTION,"End");
    }
#endif

#ifdef VHDL
  public    : void              set_port        (Vhdl * & vhdl);
#  ifdef VHDL_TESTBENCH
  public    : Signal *          get_clock       (void);
  public    : Signal *          get_reset       (void);
  public    : uint32_t          get_reset_cycle (bool active_low);

  public    : void              set_signal      (Vhdl * & vhdl);
  public    : void              get_name_vhdl   (list<string> * & list_signal);

  public    : void              testbench        (void);
  public    : void              testbench_body   (Vhdl           * & vhdl          ,
						  string             counter_name  ,
						  string             reset_name    );
  public    : void              testbench_test_ok(Vhdl           * & vhdl          );
#  endif
#endif
  public    : XML               toXML           (void);

  public    : friend ostream&   operator<<      (ostream& output_stream,
						 morpheo::behavioural::Signal & x);

  };
}; // end namespace behavioural          

  template<>           inline std::string toString<morpheo::behavioural::presence_port_t>(const morpheo::behavioural::presence_port_t& x)
  {
    switch (x)
      {
      case morpheo::behavioural::PORT_VHDL_YES_TESTBENCH_YES : return "Port  is in VHDL's model and TestBench's model" ; break;
      case morpheo::behavioural::PORT_VHDL_YES_TESTBENCH_NO  : return "Port  is in VHDL's model                      " ; break;
      case morpheo::behavioural::PORT_VHDL_NO_TESTBENCH_YES  : return "Port  is in                  TestBench's model" ; break;
      case morpheo::behavioural::PORT_VHDL_NO_TESTBENCH_NO   : return "Port  is in none model                        " ; break;
      case morpheo::behavioural::CLOCK_VHDL_YES              : return "Clock is in VHDL's model                     " ; break;
      case morpheo::behavioural::CLOCK_VHDL_NO               : return "Clock is not in VHDL's model                 " ; break;
      case morpheo::behavioural::RESET_VHDL_YES              : return "Reset is in VHDL's model                     " ; break;
      case morpheo::behavioural::RESET_VHDL_NO               : return "Reset is not in VHDL's model                 " ; break;
      default                                                : return "";                                               break;
      }
  }

}; // end namespace morpheo              

#endif
