// ### -------------------------------------------------------------- ###
// #                                                                    #
// # File       : sim2os.h                                              #
// # Authors    : Rosière Mathieu                                       #
// # Date       : 12/06/2006                                            #
// # Modif.     : dd/mm/yyyy                                            #
// # Version    : 1.0                                                   #
// #                                                                    #
// # Origin     : this description has been developed by CAO-VLSI team  #
// #              at ASIM laboratory, University Pierre et Marie Curie  #
// #              4 Place Jussieu 75252 Paris Cedex 05 - France         #
// #                                                                    #
// ### -------------------------------------------------------------- ###

/*
 * WARNING :
 * This bridge can be use, if :
 *  - sizeof between the host's cpu and the simulation's cpu is the same
 *  -
 */

#ifndef SIM2OS_H
#define SIM2OS_H

#include "service.h"
#include "service_conversion.h"
#include <iostream>
#include <map>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdarg.h>
#include "shared/soclib_segment_table.h"
#include "../endianness/endianness.h"
using namespace std;
using namespace hierarchy_memory::sim2os::service;

namespace hierarchy_memory {
  namespace sim2os {



    //******************************************************************************************
    // Interface
    //******************************************************************************************



  class param_t
  {
  public : char *                 name          ;
  public : SOCLIB_SEGMENT_TABLE * segment_table ;

  public : param_t () {};
    
  public : param_t (char *                 name       , 
		    SOCLIB_SEGMENT_TABLE * segment_table     )
    {
      this->name          = name       ;
      this->segment_table = segment_table     ;
    }    
  }; //end param_t

    class Sim2os
    {
      //=====[ Private ]==========================================================================
      //-----[ Champ ]----------------------------------------------------------------------------
    private : const char *             NAME;
    private : service_t                num_service;    // number of service
    private : map<unsigned int,void *> arguments;
    private : SOCLIB_SEGMENT_TABLE   * segment_table;
    private : unsigned long long       nb_cycle;

      //-----[ Methods ]--------------------------------------------------------------------------
    private : bool                     have_all_arguments (unsigned int);
    private : void *                   convert_address    (void *);

    private : void *                   service_open       ();
    private : void *                   service_close      ();
    private : void *                   service_read       ();
    private : void *                   service_write      ();
    private : void *                   service_time       ();
    private : void *                   service_clock      ();
    private : void *                   service_lseek      ();

      //=====[ Public ]===========================================================================
    public  : void *                   result;
    public  : int                      error; // It's errno

      //-----[ Methods ]--------------------------------------------------------------------------
    public  :                          Sim2os             (param_t);
    public  :                          ~Sim2os            ();
    public  : void *                   execute            ();
    public  : void *                   execute            (service_t);
    public  : void *                   execute            (service_t, void *, ...);
    public  : void                     parameter          (unsigned int, void *);
    public  : void                     transition         ();

      //=====[ Friend ]===========================================================================
      //-----[ Methods ]--------------------------------------------------------------------------
      friend    ostream&                 operator<<         (ostream&, Sim2os);
    };



    //******************************************************************************************
    // Implementation
    //******************************************************************************************



    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ Sim2os ]~~~~~
    Sim2os ::
    Sim2os (param_t param) :
      NAME (param.name)
    {
      segment_table = param.segment_table;
      result        = (void *)-1;
      error         =          1;
      nb_cycle      =          0;
      cout << "<" << NAME << "> Successful Instanciation" << endl;
    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ ~Sim2os ]~~~~~
    Sim2os ::
    ~Sim2os ()
    {
      cout << "<" << NAME << "> Destructor" << endl;
    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ have_all_arguments ]~~~~~
    /*
     * Test if all arguments is here
     * (don't test the coherencies of this)
     */
    bool
    Sim2os ::
    have_all_arguments (unsigned int nb_val)
    {
      //   while ((nb_val > 0) &&
      // 	 (arguments.find(nb_val) != arguments.end()))
      //     nb_val --;

      unsigned int val = nb_val;
      while (nb_val > 0)
	if (arguments.find(nb_val) != arguments.end())
	  nb_val --;
	else
	  {
	    cerr << "argument[" << nb_val << "] on " << val << ", is not find." << endl;
	    break;
	  }

      return (nb_val == 0);
    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ convert_address ]~~~~~
    /*
     * convert a address in the simulator on a address in OS
     */
    void *
    Sim2os ::
    convert_address (void * address)
    {
      void * result = segment_table->getAddrAlloc((unsigned int)address);
  
      if (result == NULL)
	{
	  cerr << "<" << NAME << "> address don't match : " << hex << (unsigned int)address << dec << endl;
	  exit(0);
	}
  
      return result;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ transition ]~~~~~
    /*
     * have a new cycle : update register cycle's depend
     */
    void 
    Sim2os ::
    transition ()
    {
      nb_cycle ++;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ execute ]~~~~~
    /*
     * execute the service with num_service in argument
     */
    void *
    Sim2os ::
    execute (service_t num)
    {
      num_service = num;

      return execute();
    }

    /*
     * execute the service with all
     * the list of arg must be finish by "NULL"
     * also the list can't have a NULL as param
     */
    void *
    Sim2os ::
    execute (service_t num, void * arg, ...)
    {
  
      num_service = num;

      va_list ap;
      va_start (ap,arg);
      unsigned int it = 1;
      while (arg != NULL)
	{
	  arguments[it] = arg;  
	  it ++;
	  arg = va_arg(ap,void *);
	}
  
      va_end(ap);
      return execute();
    }

    /*
     * execute the service without argument
     */
    void *
    Sim2os ::
    execute ()
    {
      result = NULL;

      switch (num_service)
	{
	case SERVICE_OPEN          : {result = service_open     (); break;}
	case SERVICE_CLOSE         : {result = service_close    (); break;}
	case SERVICE_READ          : {result = service_read     (); break;}
	case SERVICE_WRITE         : {result = service_write    (); break;}
	case SERVICE_TIME          : {result = service_time     (); break;}
	case SERVICE_CLOCK         : {result = service_clock    (); break;}
	case SERVICE_LSEEK         : {result = service_lseek    (); break;}
	case SERVICE_UNIMPLEMENTED :
	  {
	    cerr << "<" << NAME << "> service Unimplemented : " << num_service << endl;
	    break;
	  }

	case SERVICE_UNDEFINED     :
	default                    :
	  {
	    cerr << "<" << NAME << "> service Undefine      : " << num_service << endl;
	    break;
	  }
	}//end switch

      // Erase all elt;
      arguments.erase(arguments.begin(),arguments.end());
      return result;
    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ parameter ]~~~~~
    /*
     * Add a new parameter
     * if num_reg == 0, it's the number of service
     * else             it's a arguments
     */
    void
    Sim2os ::
    parameter (unsigned int num_reg,
	       void       * val)
    {
      if (num_reg == 0)
	num_service = int2service((int) val);
      else
	arguments[num_reg] = val;
    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ operator<< ]~~~~~
    ostream& operator<< (ostream& output, Sim2os input)
    {
      //   output << "<" << input.NAME << ">" << endl;
      //   output << "num_service : " << input.num_service << endl;

      //   for (map<unsigned int,void *>::iterator it = input.arguments.begin(); 
      //        it != input.arguments.end();
      //        it ++)
      //     output << "[" << it.first << "] : " << it.second << endl;
      return output;
    };

  };};

    // Include all service !!!
#include "service/service_open.h"
#include "service/service_close.h"
#include "service/service_read.h"
#include "service/service_write.h"
#include "service/service_time.h"
#include "service/service_clock.h"
#include "service/service_lseek.h"


#endif //!SIM2OS_H
