#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H

#include "../Common/include/Debug.h"
#include "../Cache/include/Cache.h"
#include "../Data/include/Data.h"
#include "../Endianness/include/Endianness.h"
#include "../Queue/include/Sort_Queue.h"
#include "../RamLock/include/RamLock.h"
#include "../Sim2OS/include/Sim2OS.h"
#include "../TTY/include/TTY.h"
#include "../../processor/Morpheo/Common/include/Max.h"

#include "Types.h"
#include "Environment_Parameters.h"
#include "Respons.h"
#include "Cache_Access.h"

#include <systemc.h>

namespace environment {

  typedef Respons<morpheo::Ticache_context_t, morpheo::Ticache_packet_t,morpheo::Ticache_error_t> irsp_t;
  typedef Respons<morpheo::Tdcache_context_t, morpheo::Tdcache_packet_t,morpheo::Tdcache_error_t> drsp_t;

  class Environment : public sc_module
  {
    // ===== [ Ports ]=======================================================
  public : sc_in_clk                        * CLOCK                 ;
  public : sc_in<bool>                      * NRESET                ;

  public : sc_in <morpheo::Tcontrol_t           >  *** ICACHE_REQ_VAL        ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_out<morpheo::Tcontrol_t           >  *** ICACHE_REQ_ACK        ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_in <morpheo::Ticache_context_t    >  *** ICACHE_REQ_CONTEXT_ID ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_in <morpheo::Ticache_packet_t     >  *** ICACHE_REQ_PACKET_ID  ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_in <morpheo::Ticache_address_t    >  *** ICACHE_REQ_ADDRESS    ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_in <morpheo::Ticache_type_t       >  *** ICACHE_REQ_TYPE       ;//[nb_entity][icache_dedicated_nb_port]

  public : sc_out<morpheo::Tcontrol_t           >  *** ICACHE_RSP_VAL        ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_in <morpheo::Tcontrol_t           >  *** ICACHE_RSP_ACK        ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_out<morpheo::Ticache_context_t    >  *** ICACHE_RSP_CONTEXT_ID ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_out<morpheo::Ticache_packet_t     >  *** ICACHE_RSP_PACKET_ID  ;//[nb_entity][icache_dedicated_nb_port]
  public : sc_out<morpheo::Ticache_instruction_t> **** ICACHE_RSP_INSTRUCTION;//[nb_entity][icache_dedicated_nb_port][iaccess_nb_instruction]
  public : sc_out<morpheo::Ticache_error_t      >  *** ICACHE_RSP_ERROR      ;//[nb_entity][icache_dedicated_nb_port]

  public : sc_in <morpheo::Tcontrol_t           >  *** DCACHE_REQ_VAL        ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_out<morpheo::Tcontrol_t           >  *** DCACHE_REQ_ACK        ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_in <morpheo::Tdcache_context_t    >  *** DCACHE_REQ_CONTEXT_ID ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_in <morpheo::Tdcache_packet_t     >  *** DCACHE_REQ_PACKET_ID  ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_in <morpheo::Tdcache_address_t    >  *** DCACHE_REQ_ADDRESS    ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_in <morpheo::Tdcache_type_t       >  *** DCACHE_REQ_TYPE       ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_in <morpheo::Tdcache_data_t       >  *** DCACHE_REQ_WDATA      ;//[nb_entity][dcache_dedicated_nb_port]

  public : sc_out<morpheo::Tcontrol_t           >  *** DCACHE_RSP_VAL        ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_in <morpheo::Tcontrol_t           >  *** DCACHE_RSP_ACK        ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_out<morpheo::Tdcache_context_t    >  *** DCACHE_RSP_CONTEXT_ID ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_out<morpheo::Tdcache_packet_t     >  *** DCACHE_RSP_PACKET_ID  ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_out<morpheo::Tdcache_data_t       >  *** DCACHE_RSP_RDATA      ;//[nb_entity][dcache_dedicated_nb_port]
  public : sc_out<morpheo::Tdcache_error_t      >  *** DCACHE_RSP_ERROR      ;//[nb_entity][dcache_dedicated_nb_port]

    // ===== [ Internal ]====================================================
  public : morpheo::Tcontrol_t                      ** icache_req_ack        ;
  public : morpheo::Tcontrol_t                      ** icache_rsp_val        ;
  public : morpheo::Tcontrol_t                      ** dcache_req_ack        ;
  public : morpheo::Tcontrol_t                      ** dcache_rsp_val        ;

    // ===== [ Variables ]===================================================
  private  : const sc_module_name name;
  private  : Parameters         * param;

  private  : char              ** read_iram;
  private  : char              ** read_dram;
  private  : char               * write_dram;

  private  : bool               * context_stop;    // to determine which context have send the signal stop (a same thread can send many signal)
  private  : uint32_t             nb_context_stop; // stop the simulation when all context have send the stop signal 


    // ===== [ Components ]==================================================
  private  : cache  ::Cache                * component_cache;
  private  : tty    ::TTY                 ** component_tty;
  private  : ramlock::RamLock             ** component_ramlock;
  private  : sim2os ::Sim2OS               * component_sim2os;
  private  : data   ::Data                 * component_data;
  private  : queue  ::Sort_Queue<irsp_t*> ** component_buffer_irsp;
  private  : queue  ::Sort_Queue<drsp_t*> ** component_buffer_drsp;

    // ===== [ Methods ]=====================================================

    SC_HAS_PROCESS (Environment);
    
  public   :  Environment (sc_module_name name, Parameters * param);
  public   : ~Environment (void);

  public   : bool   init           (std::string section, const char * filename, const char ** list_section);
  public   : void   reset          (void);
  public   : void   stop           (uint32_t num_context);
  public   : bool   simulation_end (void);

  public   : void   transition     (void);
  public   : void   genMoore       (void);

  public   : friend std::ostream& operator<< (std::ostream& output, Environment &x);

    
  private  : 
    template <class T>
    void itoa  (T src, char *& dest, uint32_t size)
    {
      _cout(ENVIRONMENT,"<itoa> : size : %d, ",size);
      __cout(ENVIRONMENT,"src : %.8x ",static_cast<uint32_t>(src));
      __cout(ENVIRONMENT,"dest : ");

      for (uint32_t i=0; i<size; i++)
	{
	  uint32_t j = i;
	  dest [j] = (src & 0xFF);
	  src >>= 8; // shift byte

	  __cout(ENVIRONMENT,"%.2x",0xff&static_cast<uint32_t>(dest [j]));

	}
      __cout(ENVIRONMENT,".\n");
    }
    
  private  : 
    template <class T>
    void atoi (char * src, T& dest, uint32_t size)
    {
      dest = 0;
      
      _cout(ENVIRONMENT,"<atoi> : size : %d, ",size);
      __cout(ENVIRONMENT,"src : ");

      for (uint32_t i=0; i<size; i++)
	{
	  uint32_t j=(size)-(i)-1;
	  dest <<= 8; // shift byte
	  dest |= (static_cast<T>(src [j]) & 0xFF);

	  __cout(ENVIRONMENT,"%.2x",0xff&static_cast<uint32_t>(src [j]));
	}
      __cout(ENVIRONMENT," dest : %.8x\n",static_cast<uint32_t>(dest));
    }
  };
};

#endif
