/****************************************************************************
**			TAU Portable Profiling Package			   **
**			http://www.acl.lanl.gov/tau		           **
*****************************************************************************
**    Copyright 1997  						   	   **
**    Department of Computer and Information Science, University of Oregon **
**    Advanced Computing Laboratory, Los Alamos National Laboratory        **
****************************************************************************/
// Note : This is a trivial application that does nothing. It is written to 
// illustrate how to insert TAU Profiling Instrumentation in an application
// that uses templates.
// Tests the classes, functions, templated classes, templated functions with
// profiling. 
// Some compilers donot support member templates of class templates yet, so 
// NEWCC is defined so that compilation doesn't break on most systems.

#include "include.h"
#include "Vector.h"

/* Chad Globals */
HPCxx_Sync<int> g_wait;
HPCxx_GlobalPtr<CVector> g_GPCVec;

// This function releases the main procees waiting on a sync variable;
int Kill(int i) 
{
  g_wait = i;
  return 0;
}

// Function to access the global pointer
HPCxx_GlobalPtr<CVector> GetGP() 
{
  return g_GPCVec;
}


TAU_REGISTER_EVENT(getdata, "Get Data values");

class NoTempl { // Simple class with Get and Set methods
  private :
    int Data;
  public : 
    NoTempl() {
      TAU_PROFILE("NoTempl::NoTempl()","void ()", TAU_USER); 
    };
    int GetData (void) { 
      TAU_PROFILE("NoTempl::GetData()","void ()", TAU_UTILITY | TAU_USER);
      TAU_EVENT(getdata,(double) Data);
      return Data;
    }
    int SetData (int d) {
      TAU_PROFILE("NoTempl::SetData()", "int (int)", TAU_SPARSE | TAU_USER);
      Data = d;
      return Data;
    }

#ifdef NEWCC
    // Templated member function in a non-templated class.
    template <class Dim> 
    int SetDataTemplMem (Dim dimen, int dat) {

      TAU_TYPE_STRING(p1, "int (" + CT(dimen) + ", int)" ); 
      TAU_PROFILE("NoTempl::SetDataTemplMem()", p1, 
	TAU_UTILITY | TAU_FIELD | TAU_IO | TAU_USER);
//      PoomaInfo::abort("Exiting from NoTempl::SetDataTemplMem");
      Data = dat * dimen;
      return Data;
    }
#endif //NEWCC

    ~NoTempl() {
      TAU_PROFILE("NoTempl::~NoTempl","void ()", TAU_REGION | TAU_USER);
     }
};


template <class T>
class TemplClass { // Simple class template with Get and Set methods
  private :
    T Data;
  public :
    TemplClass() { 
      TAU_TYPE_STRING(taustr, CT(*this) + " void (void)");
      TAU_PROFILE("TemplClass::TemplClass()", taustr, TAU_USER);
    };
    T GetData(void) {
      TAU_TYPE_STRING(taustr, CT(*this) + " void (void)");
      TAU_PROFILE("TemplClass::GetData()", taustr, TAU_ACLMPL | TAU_USER);
      TAU_EVENT(getdata,(double) Data * 2);
      return Data;
    }
    T SetData(T d) {
      TAU_TYPE_STRING(taustr, CT(*this) + " void (void)");
      TAU_PROFILE("TemplClass::SetData()", taustr, TAU_FFT | TAU_USER);
      Data = d;
      return Data;
    }
   
#ifdef NEWCC
    // member template of a class template
    template <class Dim> 
    T SetDataTemplMem (Dim dimen, T dat) {

      TAU_TYPE_STRING(p1, CT(Data) + " (" + CT(dimen) + ", " + CT(dat) + " )" );
      TAU_PROFILE("TemplClass::SetDataTemplMem()", p1, TAU_USER1 | TAU_USER);

      Data = dat * dimen;
      return Data;
    }
#endif //NEWCC

    ~TemplClass() { 
	
      TAU_TYPE_STRING(taustr, CT(*this) + " void (void)");
      TAU_PROFILE("TemplClass::~TemplClass()", taustr, TAU_USER2 | TAU_USER);
      // Can't get address of constructor or destructor in C++. Using class name
    }
};

// Simple non-templated function  equivalent to getpid()
int func_no_templ(void) {
  TAU_PROFILE("func_no_templ","int ()", TAU_USER3 | TAU_USER);

  int x;

  x = getpid();
  return x;
}

// Function template returns size of the type with which it is instantiated
template <class T>
int func_templ( T x) {
  TAU_TYPE_STRING(buf, "int (" + CT(x) + " )" );
  TAU_PROFILE("func_templ", buf, TAU_PAWS3 | TAU_USER);

  return sizeof(x);
}



int main(int argc, char *argv[])
{
  TAU_PROFILE_INIT(argc, argv);
  TAU_PROFILE_SET_NODE(0); // sequential code 
  TAU_PROFILE("main", "int (int, char **)", TAU_DEFAULT);
  TAU_PROFILE_TIMER(notemp, "main-notemp-loop", "int (int, char **)", TAU_USER1 | TAU_USER);
  TAU_PROFILE_TIMER(templ, "main-temp", "int (int, char **)", TAU_USER1 | TAU_USER);
  


// Testing data 
  NoTempl X;
  TemplClass<double> Y;
  TemplClass<char> C;
  /*
  int idim = 1;
  double ddim = 3.0;
  */



  // Testing the classes and functions defined above 
  X.SetData(34);
  if (X.GetData() != 34) { 
    cout <<"NoTempl Class Test failed X.GetData() = "<< X.GetData() << endl;
  }
  else
    cout <<"Test Successful : NoTempl::GetData() " << endl;


  // Testing TemplClass
  Y.SetData(34);
  if (X.GetData() != (int) Y.GetData() ) {
    cout <<"Test failed - X.GetData = "<< X.GetData() << " Y.GetData = "
      << Y.GetData() << endl;
  }
  else
    cout << "Test Successful : TemplClass<double>::GetData() " << endl;
  

  // Calling TemplClass with another instantiation 
  C.SetData(34);
  if (X.GetData() != (int) C.GetData() ) {
    cout <<"Test failed - X.GetData = "<< X.GetData() << " C.GetData = "
      << C.GetData() << endl;
  }
  else
	cout << "Test Successful : TemplClass<char>::GetData() " << endl;

  TAU_PROFILE_START(notemp);
#ifdef NEWCC
  // Testing NoTempl::SetDataTemplMem <int,int> function
  if (X.SetDataTemplMem(idim, 100) != (int) (idim * 100)) {
    cout<<"X.SetDataTemplMem test failed X.SetDataTemplMem(idim, 100) = "<< 
      X.SetDataTemplMem(idim,100) << " idim = "<< idim << endl; 
  }
  else	
    cout << "Test Successful : NoTempl::SetDataTemplMem<int,int>() " << endl;

  // Testing NoTempl::SetDataTemplMem <double,int> function
  if (X.SetDataTemplMem(ddim, 100) != (int) (ddim * 100)) {
    cout<<"X.SetDataTemplMem test failed X.SetDataTemplMem(ddim, 100) = "<< 
      X.SetDataTemplMem(ddim,100) << " ddim = "<< ddim << endl; 
  }
  else	
    cout << "Test Successful : NoTempl::SetDataTemplMem<double,int>() " << endl;

#endif //NEWCC
  TAU_PROFILE_STOP(notemp);
  // Testing func_no_templ
  if (getpid() != func_no_templ()) {
    cout <<"func_no_templ test failed func_no_templ returns "<< func_no_templ() << endl;
  }
  else
    cout <<"Test Successful : func_no_templ() " << endl;

  TAU_PROFILE_START(templ);
  // Testing func_templ
  if (sizeof(double) != func_templ (Y.GetData())) {
    cout <<"Test Failed sizeof (double) = "<< sizeof(double) << 
      " func_templ(Y.GetData()) returns "<< func_templ(Y.GetData()) << endl;
  }
  else
    cout <<"Test Successful : func_templ<double>() "<<endl;
 
  if (sizeof(char) != func_templ (C.GetData())) {
    cout <<"Test Failed sizeof (double) = "<< sizeof(double) << 
      " func_templ(C.GetData()) returns "<< func_templ(C.GetData()) << endl;
  }
  else
    cout <<"Test Successful : func_templ<char>() "<<endl;

#ifdef NEWCC
  // Testing TemplClass::SetDataTemplMem <int,double> function
  if (Y.SetDataTemplMem(idim, 100) != (double) (idim * 100.0)) {
    cout<<"Y.SetDataTemplMem test failed Y.SetDataTemplMem(idim, 100) = "<< 
      Y.SetDataTemplMem(idim,100) << " idim = "<< idim << endl; 
  }
  else	
    cout <<
      "Test Successful : TemplClasss<double>::SetDataTemplMem<int,double>()" 
      << endl;

  // Testing TemplClass::SetDataTemplMem <double,double> function
  if (Y.SetDataTemplMem(ddim, 10) != (double) (ddim * 10)) {
    cout<<"Y.SetDataTemplMem test failed Y.SetDataTemplMem(ddim, 100) = "<< 
      Y.SetDataTemplMem(ddim,100) << " ddim = "<< ddim << endl; 
  }
  else	
    cout << 
      "Test Successful : TemplClasss<double>::SetDataTemplMem<double,double>() "      << endl;
  
  // Testing TemplClass::SetDataTemplMem <int,char> function
  if (C.SetDataTemplMem(idim, 100) != (char) (idim * 100)) {
    cout<<"C.SetDataTemplMem test failed C.SetDataTemplMem(idim, 100) = "<< 
	C.SetDataTemplMem(idim,100) << " idim = "<< idim << endl; 
  }
  else	
    cout << "Test Successful : TemplClasss<char>::SetDataTemplMem<int,char>() " << endl;

  // Testing TemplClass::SetDataTemplMem <double,char> function
  if (C.SetDataTemplMem(ddim, 10) != (char) (ddim * 10)) {
    cout<<"C.SetDataTemplMem test failed C.SetDataTemplMem(ddim, 10) = "<< 
	C.SetDataTemplMem(ddim,10) << " ddim = "<< ddim << endl; 
  }
  else	
    cout << 
      "Test Successful : TemplClasss<char>::SetDataTemplMem<double,char>() " 
      << endl;
#endif //NEWCC

  /**********************************************************************************************************/
  /* Chad Code */
  /**********************************************************************************************************/
  int port = 2377;
  HPCxx_Group *g;
  CVector *vec = new CVector(&TheFunctionDB());
  hpcxx_init(argc, argv, g) ;

  vec->RegisterClass();
  HPCxx_GlobalPtr<CVector> GPVec(vec);
  g_GPCVec = GPVec;

  // FUCK -- this doesn't work???????
  // vector<int*> *int_vector = new vector<int*>;
  //  HPCxx_GlobalPtr<vector<int*>*> gp;
  //  gp = int_vector;
  
  //  vector<FunctionInfo*> func_db = TheFunctionDB(); 
  //  hpcxx_registerClass((vector<FunctionInfo*> *)NULL);
  //  HPCxx_GlobalPtr<vector<FunctionInfo*>*> GpFuncDB(func_db); 

  cout << "Vector Size == " << vec->Size() << endl;

  hpcxx_id_t kill_id = hpcxx_register(Kill,20);
  hpcxx_id_t get_gp  = hpcxx_register(GetGP,21);
  vec->RegisterMethods(22);

  hpcxx_allowAttach(port,NULL,NULL);

  int sync_val = 1;
  g_wait.read(sync_val); // Wait to be released  

  hpcxx_exit(g);
  TAU_PROFILE_STOP(templ);
  return 0;
}


