#ifndef DATA_H
#define DATA_H

#include <stdint.h>
#include <iostream>
#include "segment.h"
#include "shared/soclib_segment_table.h"

using namespace std;

namespace hierarchy_memory {
namespace data             {

  class entity_t
  {
  public : bool        present;
  public : segment_t * segment;
    
  public : entity_t (bool present)
    {
      this->present = present;
    }
    
  public : entity_t (bool        present ,
		     segment_t * segment )
    {
      this->present = present;
      this->segment = segment;
    }

    friend ostream& operator<< (ostream& output_stream, const entity_t & x)
    {
      output_stream << x.present;
      if (x.present == true)
	output_stream << " " << *x.segment;

      return output_stream;
    }
  }; //end entity_t

  class param_t
  {
  public : char *                 name       ;
  public : uint32_t               max_seg    ;
  public : unsigned int           globalIndex;
  public : unsigned int           localIndex ;
  public : SOCLIB_SEGMENT_TABLE * segtab     ;

  public : param_t (char                 * name       ,
		    uint32_t               max_seg    ,
		    unsigned int           globalIndex,
		    unsigned int           localIndex ,
		    SOCLIB_SEGMENT_TABLE * segtab     )
    {
      this->name        = name       ;
      this->max_seg     = max_seg    ;
      this->globalIndex = globalIndex;
      this->localIndex  = localIndex ;
      this->segtab      = segtab     ;
    }    
  }; //end param_t
  
  class Data
  {
  private   : char *         name;
  protected : const uint32_t max_seg;
  protected : uint32_t       nb_seg;
  protected : segment_t   ** segment;

  public : Data (param_t param) :
    max_seg (param.max_seg)
    {
      uint32_t size_name = strlen(param.name)+1;
      name               = new char [size_name];
      strncpy(name,param.name,size_name);

      std::list<SEGMENT_TABLE_ENTRY>           seglist = param.segtab->getSegmentList(param.globalIndex,param.localIndex);
      std::list<SEGMENT_TABLE_ENTRY>::iterator iter;
      nb_seg = 0;

      segment = new segment_t * [max_seg];

      for (iter = seglist.begin() ; iter != seglist.end() ; ++iter , nb_seg ++)
	{
	  SEGMENT_TABLE_ENTRY entry = *iter;
	  if (nb_seg >= max_seg)
	    {
	      cerr << "The number of seg can't be higher that " << max_seg << endl;
	      exit (1);
	    }
	  segment [nb_seg] = new segment_t (entry);
	  
	  param.segtab->setAddrAlloc(entry.getBase(),(void *)(segment [nb_seg]->data_addr()));
	}
    }

    //*****[ init ]*****
  public : bool init (char * section, const char * filename, const char ** list_section)
    {
      for (uint32_t it = 0; it < nb_seg; it ++)
	if (segment[it]->test(section) == true)
	  return segment[it]->init(filename,list_section);

      return false;
    }
    
    //*****[ read ]*****
    // Read a lot of byte
  public : bool read (uint32_t address, uint32_t size, char * & data_dest)
    {
      uint32_t num_seg = 0;
      bool     res     = false;
      for (num_seg = 0; num_seg < nb_seg; num_seg ++)
	if (segment[num_seg]->test (address,size) == true)
	  {
	    segment[num_seg]->read (address,size,data_dest);
	    res = true;
	    break;
	  }

      return res;
    }

    //*****[ entity ]*****
  public : entity_t entity (uint32_t address, uint32_t size)
    {
      for (uint32_t num_seg = 0; num_seg < nb_seg; num_seg ++)
	if (segment[num_seg]->test (address,size) == true)
	  return entity_t (true,segment[num_seg]);
      
      return entity_t (false);
    }

  public : entity_t entity (char * name)
    {
      for (uint32_t num_seg = 0; num_seg < nb_seg; num_seg ++)
	if (segment[num_seg]->test(name) == true)
	  return entity_t (true,segment[num_seg]);
      
      return entity_t (false);
    }

    //*****[ write ]*****
    // Write a lot of byte
  public : bool write (uint32_t address, uint32_t size, char * & data_src)
    {
      uint32_t num_seg = 0;
      bool     res     = false;
      for (num_seg = 0; num_seg < nb_seg; num_seg ++)
	if (segment[num_seg]->test (address,size) == true)
	  {
	    segment[num_seg]->write (address,size,data_src);
	    res = true;
	    break;
	  }

      return res;
    }

  public    : friend ostream& operator<< (ostream& output_stream, const Data & x)
    {
      output_stream << "<" << x.name << ">" << endl
		    << "  * nb_seg  : " << x.nb_seg  << endl 
		    << "  * max_seg : " << x.max_seg << endl;

      for (uint32_t it = 0; it < x.nb_seg; it ++)
	output_stream << *x.segment[it] << endl;
      return output_stream;
    };
  };

};}; //end namespace
#endif //!DATA_H
