///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Copyright (C) 2006 by Intel Corporation and Carnegie Mellon University    //
// Contacts: casey.j.helfrich @ intel.com                                    //
//           bdr @ cs.cmu.edu                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#ifndef __CODEMODULE_HXX
#define __CODEMODULE_HXX

#include "Primitives.hxx"
#include "StateFile/Modules.hxx"

#include "Debugging.hxx"

class Catom;
class CatomWorld;
class CatomSim;

using namespace std;

extern CatomWorld *worldPtr;


// useful accessors

#define HOSTCATOM (worldPtr->catomHash[hostCatom]->C)
#define HOSTCATOMSIM (worldPtr->catomHash[hostCatom])


// Subclass this class to implement code modules
// Override the constructor to register your mailbox handlers
// Override newTick() to implement code that's run at the start of each tick

CLASSNP(CodeModule)
protected:
  IVAR(catomID,hostCatom);
  string name_;
  StateFile::Module* myModule_;
  
public:
  // Override the constructor to register mailbox handlers and do basic 
  // variable initialization.  Don't rely upon the world being initialized 
  // at this point, do that kind of initialization in simulationStart().
  
  CodeModule();
  CodeModule(catomID _hostCatom);
  
  virtual ~CodeModule();
  
  // Called after the world is initialized but before the first tick
  virtual void simulationStart();
  
  // Called at the beginning of every tick, before message processing
  virtual void newTick();
  
  // Called at the end of every tick, after message processing
  virtual void endTick();

  // Called once per CodeModule in world.loadWorld() before we read
  // the worldFile header
  virtual StateFile::Module* StateFileConstructor();

  // Optionally called if world file contains data for this module
  // (StateFile::File::modulesIn has an entry that matches me)
  virtual void loadModule();

  // Optionally called if worldPtr->save flag is set
  // Give the code module a chance to write state to the world file
  // at the appropriate time.
  virtual void saveModule();
  
  // Called after the final tick; great place to dump statistics
  virtual void simulationEnd();

  // Oracle thread; one instance per named module type; run to yield
  virtual void oracle();

  // Continuous running thread; one per catom; run to yield
  virtual void thread();

};


void startCodeModuleOracle( CodeModule* m );
void oracleYield();
void oracleExit();

// This class is used to automatically provide an ID at runtime to 
// each CodeModule defined and linked in.  This should not be directly used.
class CodeModuleID {
public:
  int value;
  static int max_value;
  CodeModuleID() { value = ++max_value; }
};


// This class is used to automatically create a linked list of all
// linked-in CodeModules at runtime.  This will avoid needing to edit
// any core simulator files (ie, CodeModuleList.hxx) when adding new modules
class CodeModuleListEntry {
private:
  string ModuleName;
  CodeModule* (*getNewInstanceFn) ( catomID );
  CodeModuleListEntry* next;
  static CodeModuleListEntry* ModuleList;
public:
  static CodeModule* getModuleByName( string nm, catomID c, int* id=0 ) {
    CodeModuleListEntry* it;
    int k = CodeModuleID::max_value;
    for ( it = ModuleList; it != 0; it = it->next ) {
      if (it->ModuleName==nm) {
        if (id) *id=k;
	CodeModule* retVal = ( (*(it->getNewInstanceFn))( c ) );
	return retVal;
      }
      k--;
    }
    return 0;
  }

  CodeModuleListEntry( string name, CodeModule* (*fn)( catomID ), string module, string args ) 
        : ModuleName( name ), getNewInstanceFn( fn ) {
    this->next = ModuleList;
    ModuleList=this;
    cerr << "CodeModule: Declared \"" << name << "\" -> " << module << " with args: " << args << "\n";
  }
};


// Supporting global data for automatic generation of available CodeModules
namespace CODE_MODULE_DECLARATIONS {
  extern int* module_id_remap;
}


// CodeModule prototype: must be used in .hxx file containing prototype of
// new CodeModule
// 1st parameter is name -- this is the same name specified in .exp files
//   no quotes, must be valid for inclusion as part of function/variable name
// 2nd is CodeModule class name; 
//   NOTE: one can define multiple named modules using the same actual class, 
//   e.g., for multiple hierarchies
// optional 3rd,4th,... are additional parameters to CodeModule constructor
//   in addition to catomID; these cannot be changed per catom
// Example: CODE_MODULE_PROTOTYPE( Hierarchy2, Hierarchy, "Test2" );
// Example: CODE_MODULE_PROTOTYPE( HexRotate, HexRotate );

#define CODE_MODULE_PROTOTYPE( name, module, args... )    \
class module;    \
namespace CODE_MODULE_DECLARATIONS {    \
  typedef module * MODULE_TYPE_ ## name;    \
  extern CodeModuleID CodeModuleID_ ## name;    \
};


// CodeModule declaration:  must be used in .cxx file defining new CodeModule
// parameters are same as for CODE_MODULE_PROTOTYPE, see above
// Example: CODE_MODULE_DECLARATION( Hierarchy2, Hierarchy, "Test2" );
// Example: CODE_MODULE_DECLARATION( HexRotate, HexRotate );

#define CODE_MODULE_DECLARATION( name, module, args... )    \
namespace CODE_MODULE_DECLARATIONS {    \
  static CodeModule* getNewInstance_ ## name ( catomID c ) {    \
    return new module( c , ## args );    \
  }    \
  static CodeModuleListEntry    \
    CodeModuleListEntry_ ## name    \
    ( #name, getNewInstance_ ## name, #module, #args );    \
  CodeModuleID CodeModuleID_ ## name;    \
}; 



// CODE_MODULE and REMOTE_CODE_MODULE provide access to other modules
// on the local catom and specified remote catom, respectively.
// The name is the module name as specified in .exp files
// The return type is a pointer for the appropriate CodeModule class
// The remote version is magic, and should not be used for "real" code

#define REMOTE_CODE_MODULE( catomid, name )    \
  (( CODE_MODULE_DECLARATIONS::MODULE_TYPE_ ## name )    \
     worldPtr->catomHash[ ( catomid ) ]->codeModules    \
       [ CODE_MODULE_DECLARATIONS::module_id_remap    \
           [ CODE_MODULE_DECLARATIONS::CodeModuleID_ ## name .value ] ] )

#define CODE_MODULE( name ) REMOTE_CODE_MODULE( hostCatom, name )



#endif  /* __CODEMODULE_HXX  */

