#ifndef SEGMENT_H
#define SEGMENT_H

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

using namespace std;

extern "C" void loadexec(void **emem, int *esize, int *eoffset, const char *file, const char ** sections);


namespace hierarchy_memory {
namespace data             {
  
  class segment_t
  {
  protected : uint32_t      base;
  protected : uint32_t      size;
  protected : bool          uncached;
  protected : char        * name;
  protected : char        * data;
  protected : type_target_t type;
  protected : uint32_t      index;

  public : segment_t ()
    {
      base     = 0;
      size     = 0;
      uncached = false;
      name     = NULL;
      data     = NULL;
      type     = TYPE_MEMORY;
      index    = 0;
    }

  public : segment_t (SEGMENT_TABLE_ENTRY segment)
    {
      uint32_t size_name = strlen (segment.getName())+1;

      base     = segment.getBase();
      size     = segment.getSize();
      uncached = segment.getUncached();
      name     = new char [size_name];
      name     = strncpy (name, segment.getName(),size_name);
      data     = new char [size];
      type     = TYPE_MEMORY;
      index    = 0;
    };

    //*****[ init ]*****
  public : bool init (const char * filename,const  char ** sections)
    {
      void * ptab;
      int    size   = this->size;
      int    offset = 0;

      loadexec(&ptab,&size,&offset,filename,sections);

      cout << "    - size         : " << size         << endl;
      cout << "    - offset       : " << offset       << endl;

      if (size > (int)this->size)
	{
	  cerr << "<segment.init> : segment " << name << " is to small : size is " << this->size << " and requiert is " << size << endl;
	  return false;
	}
      
      memcpy(data, ptab, size);

      free  (ptab);
      
      return true;
    }

    //*****[ define_target ]*****
  public : void define_target (type_target_t type, uint32_t index)
    {
      this->type  = type;
      this->index = index;
    }

    //*****[ getType ]*****
  public : type_target_t getType ()
    {
      return type;
    }

    //*****[ getIndex ]*****
  public : uint32_t getIndex ()
    {
      return index;
    }

    //*****[ getBase ]*****
  public : uint32_t getBase ()
    {
      return base;
    }

    //*****[ getSize ]*****
  public : uint32_t getSize ()
    {
      return size;
    }

    //*****[ getUncached ]*****
  public : uint32_t getUncached ()
    {
      return uncached;
    }

    //*****[ data_addr ]*****
    // Return the data address
  public : char * data_addr ()
    {
      return data;
    }

    //*****[ test ]*****
  public : bool test (char * name)
    {
      return (strcmp(this->name,name) == 0);
    }

  public : bool test (uint32_t address, uint32_t size)
    {
      bool res = (((address     ) >=  this->base           ) && 
		  ((address+size) <= (this->base+this->size)));

      return res;
    }

    //*****[ read ]*****
  public : void read (uint32_t address, uint32_t size, char * & data_dest)
    {
      for (uint32_t it = 0; it < size ; it ++)
	data_dest [it] = data[address-base+it];
    }

    //*****[ write ]*****
  public : void write (uint32_t address, uint32_t size, char * & data_src)
    {
      for (uint32_t it = 0; it < size ; it ++)
	data[address-base+it] = data_src [it];
    }

    friend ostream& operator<< (ostream& output_stream, const segment_t & x)
    {
      output_stream << "Segment \"" << x.name << "\""     << endl
		    << hex
		    << "  * base     : " << setw(8) << setfill('0') << x.base << endl
		    << "  * size     : " << setw(8) << setfill('0') << x.size << endl
		    << dec
		    << "  * uncached : " << x.uncached    << endl
		    << "  * type     : " << x.type        << endl;
	
      return output_stream;
    };
  };

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