#ifndef EVARCLASSCONVERTER_H
#define EVARCLASSCONVERTER_H

#include "eutils.h"

class evar;
class evarBase;

class eclassConverterBase
{
 public:
  virtual ~eclassConverterBase(){}
  virtual evarBase *operator()(evarBase* from)=0;
};

#include <typeinfo>
using namespace std;

class estr;

template <class T,size_t (*)(const estr&)>
class estrhashof;

void addConverter(const type_info& to,const type_info& from,eclassConverterBase* converter);
eclassConverterBase* getConverter(const type_info& to,const type_info& from);


///////////////////////////////////////////////////////////////////////////////
//
//  Class Converters
//
template <class T1,class T2>
class eclassConverterCast : public eclassConverterBase
{
 public:
  evarBase *operator()(evarBase* from);
};

template <class T1,class T2>
class eclassConverterDynamicCast : public eclassConverterBase
{
 public:
  evarBase *operator()(evarBase* from);
};

/*
template <class T1,class T2>
class eclassConverterReference : public eclassConverterBase
{
 public:
  evarBase *operator()(evarBase* from);
};
*/
template <class T1,class T2>
class eclassConverterPointer : public eclassConverterBase
{
 public:
  evarBase *operator()(evarBase* from);
};
template <class T1,class T2>
class eclassConverterObject : public eclassConverterBase
{
 public:
  evarBase *operator()(evarBase* from);
};





template <class T1,class T2>
class eclassConverterUserFunc : public eclassConverterBase
{
 public:
  T1 (*userFunc)(T2);
  eclassConverterUserFunc(T1 (*userFunc)(T2));
  evarBase *operator()(evarBase* from);
};


template <class T1,class T2>
class eclassConverterMethod : public eclassConverterBase
{
 public:
  T1 (T2::*method)() const;
  eclassConverterMethod(T1 (T2::*method)() const);
  evarBase *operator()(evarBase* from);
};


#define epregisterClassConverterCast(t1,t2)  addConverter(typeid(t1),typeid(t2),new eclassConverterCast<t1,t2>)
#define epregisterClassConverterCast2(t1,t2) { epregisterClassConverterCast(t1,t2); epregisterClassConverterCast(t2,t1); }

#define epregisterClassConverterDynamicCast(t1,t2)  addConverter(typeid(t1),typeid(t2),new eclassConverterDynamicCast<t1,t2>)

//#define epregisterClassConverterReference(t1) addConverter(typeid(t1&),typeid(t1),new eclassConverterReference<t1&,t1>)
//#define epregisterClassConverterConstReference(t1) addConverter(typeid(const t1&),typeid(t1),new eclassConverterReference<const t1&,t1>)
#define epregisterClassConverterPointer(t1,t2) addConverter(typeid(t1),typeid(t2),new eclassConverterPointer<t1,t2>)
#define epregisterClassConverterObject(t1,t2) addConverter(typeid(t1),typeid(t2),new eclassConverterObject<t1,t2>)

#define epregisterClassConverterMethod(t1,t2,m) addConverter(typeid(t1),typeid(t2),new eclassConverterMethod<t1,t2>(&t2::m))


#include "evar.h"


template <class T1,class T2>
evarBase *eclassConverterCast<T1,T2>::operator()(evarBase* from)
{
  return(new evarTypeClean<T1>(new T1((T1)*dynamic_cast<evarType<T2>*>(from)->object )));
}

template <class T1,class T2>
evarBase *eclassConverterDynamicCast<T1,T2>::operator()(evarBase* from)
{
  return(new evarType<T1>(dynamic_cast<T1*>(dynamic_cast<evarType<T2>*>(from)->object)) ); //BUG: separate evarType objects pointing to same object, must be careful with auto-clean
}

/*
template <class T1,class T2>
evarBase *eclassConverterReference<T1,T2>::operator()(evarBase* from)
{
  return(new evarType<T1>(dynamic_cast<evarType<T2>*>(from)->object));
}
*/

template <class T1,class T2>
evarBase *eclassConverterPointer<T1,T2>::operator()(evarBase* from)
{
  return(new evarTypeClean<T1>(new T1(dynamic_cast<evarType<T2>*>(from)->object)));
}

template <class T1,class T2>
evarBase *eclassConverterObject<T1,T2>::operator()(evarBase* from)
{
  return(new evarTypeClean<T1>(new T1(*dynamic_cast<evarType<T2>*>(from)->object)));
}








template <class T1,class T2>
eclassConverterUserFunc<T1,T2>::eclassConverterUserFunc(T1 (*_userFunc)(T2)): userFunc(_userFunc) {}

template <class T1,class T2>
evarBase *eclassConverterUserFunc<T1,T2>::operator()(evarBase* from)
{
  return(new evarTypeClean<T1>(new T1( userFunc( *dynamic_cast<evarType<T2>*>(from)->object ) )));
}


template <class T1,class T2>
eclassConverterMethod<T1,T2>::eclassConverterMethod(T1 (T2::*_method)() const): method(_method) {}

template <class T1,class T2>
evarBase *eclassConverterMethod<T1,T2>::operator()(evarBase* from)
{
  return(new evarTypeClean<T1>(new T1((dynamic_cast<evarType<T2>*>(from)->object->*method)())) );
}

#endif

