#ifndef ECLASS_DEC_H
#define ECLASS_DEC_H

#include "eutils.h"

///////////////////////////////////////////////////////////////////////////////
//
//  Classes
//


class estr;
class evar;
template <class T>
class earray;
typedef earray<evar> evararray;



class eclassPropertyBase;
class eclassMethodBase;

#include "efunc_dec.h"
#include "evartype.h"
#include "estrhashof.h"

#include <iostream>
using namespace std;

class eclassBase
{
 public:
  estr name;
  estrhashof<eclassPropertyBase> properties;
  estrhashof<ebasicarray<eclassMethodBase*> > methods;
  estrhashof<eclassBase*> parents;

  earray<efunc> create;

  virtual ~eclassBase(){}

  virtual estr getName()=0;
  virtual const type_info& getTypeid()=0;

  virtual bool hasCout() { return(false); }

  virtual evarBase* fnew() { return(0x00); }
  virtual void fjson_serial(evarBase* var,estr& data) {}
  virtual void fserial(evarBase* var,estr& data) {}
  virtual int funserial(evarBase* var,const estr& data,int i) { return(i); }
  virtual void fserialf(evarBase* var,const efile& f) {}
  virtual bool funserialf(evarBase* var,const efile& f) { return(true); }
  virtual ostream& fcout(ostream& stream,evarBase* var) { stream<<"<"<<getName()<<">"; return(stream); }
  virtual evarBase* fcopy(evarBase* var) { return(0x00); }
  virtual void fassign(evarBase* to,evarBase* from) {}
};

template <class T>
class eclass : public eclassBase
{
 public:
  eclass(): pfcout(0x00),pfcopy(0x00),pfassign(0x00),pfserial(0x00),pfunserial(0x00),pfserialf(0x00),pfunserialf(0x00),pfjson_serial(0x00) {}

  const type_info& getTypeid(){ return(typeid(T)); }
  estr getName(){ return(typeid(T).name()); }

  ostream& (*pfcout)(ostream&,const T&);
  T* (*pfcopy)(const T&);
  void (*pfassign)(T&,const T&);

  void (*pfjson_serial)(const T&,estr&);
  void (*pfserial)(const T&,estr&);
  int (*pfunserial)(T&,const estr&,int i);
  void (*pfserialf)(const T&,const efile&);
  bool (*pfunserialf)(T&,const efile&);

  virtual bool hasCout() { if (pfcout) return(true); return(false); }

  virtual evarBase* fcopy(evarBase* var){ if (pfcopy) { return(new evarType<T>((*pfcopy)(*dynamic_cast<evarType<T>*>(var)->object))); } return(0x00); }
  virtual void fassign(evarBase* to,evarBase* from){ if (pfassign) (*pfassign)(*dynamic_cast<evarType<T>*>(to)->object,*dynamic_cast<evarType<T>*>(from)->object); }
  virtual ostream& fcout(ostream& stream,evarBase* var){ if (pfcout) { return((*pfcout)(stream,*dynamic_cast<evarType<T>*>(var)->object)); } stream << "<" << getName() << ">"; return(stream); }

  virtual int funserial(evarBase* var,const estr& data,int i) { if (pfunserial) return((*pfunserial)(*dynamic_cast<evarType<T>*>(var)->object,data,i)); return(i); }
  virtual void fserial(evarBase* var,estr& data) { if (pfserial) (*pfserial)(*dynamic_cast<evarType<T>*>(var)->object,data); }
  virtual bool funserialf(evarBase* var,const efile& f) { if (pfunserialf) return((*pfunserialf)(*dynamic_cast<evarType<T>*>(var)->object,f)); return(true); }
  virtual void fserialf(evarBase* var,const efile& f) { if (pfserialf) (*pfserialf)(*dynamic_cast<evarType<T>*>(var)->object,f); }
  virtual void fjson_serial(evarBase* var,estr& data) { if (pfjson_serial) (*pfjson_serial)(*dynamic_cast<evarType<T>*>(var)->object,data); else data+="null"; }
};

void setClasses(estrhashof<eclassBase>& classes);
void setClassNames(estrhashof<eclassBase*>& classes);

estrhashof<eclassBase>& getClasses();
estrhashof<eclassBase*>& getClassNames();



#define GETCLASS(c,T) { if (!getClasses().exists(typeid(T).name())){                         \
                          c = new eclass<T>;                                                 \
                          getClasses().addref(typeid(T).name(),c);                           \
                        }else{                                                               \
                          c = dynamic_cast< eclass<T>* >( &getClasses().values(typeid(T).name()) ); \
                        } }

#define epregisterFuncMethod(primitive,func,name)  epregisterFuncMethod2<primitive>(func,name)

#define epregisterClassName(c)                   epregisterClassName2<c>(#c)

#define epregisterClassMethod(c,m)               { epregisterClassName2<c>(#c); epregisterClassMethod3<c>(&c::m,#m); }
#define epregisterClassMethodA(c,m,rm)           { epregisterClassName2<c>(#c); epregisterClassMethod3<c>(&c::m,rm); }
#define epregisterClassMethodD(c,m,defargs)      { epregisterClassName2<c>(#c); epregisterClassMethod3<c>(&c::m,#m,defargs); }
#define epregisterClassMethodI(c,m,i)            { epregisterClassName2<c>(#c); epregisterClassMethod3<c>(&c::m,#m,evararray(),i); }
#define epregisterClassMethodDI(c,m,defargs,i)   { epregisterClassName2<c>(#c); epregisterClassMethod3<c>(&c::m,#m,defargs,i); }
#define epregisterClassMethod2(c,m,r,a)          { epregisterClassName2<c>(#c); epregisterClassMethod3<c>((r(c::*)a)&c::m,#m); }
#define epregisterClassMethod2A(c,m,rm,r,a)      { epregisterClassName2<c>(#c); epregisterClassMethod3<c>((r(c::*)a)&c::m,rm); }
#define epregisterClassMethod2AD(c,m,rm,r,a,defargs)      { epregisterClassName2<c>(#c); epregisterClassMethod3<c>((r(c::*)a)&c::m,rm,defargs); }
#define epregisterClassMethod2D(c,m,r,a,defargs) { epregisterClassName2<c>(#c); epregisterClassMethod3<c>((r(c::*)a)&c::m,#m,defargs); }
#define epregisterClassMethod4(c,m,r,a,n)        { epregisterClassName2<c>(#c); epregisterClassMethod3<c>((r(c::*)a)&c::m,n); }

#define epregisterClassProperty(c,p)       { epregisterClassName2<c>(#c); epregisterClassProperty2<c>(&c::p,#p); }
#define epregisterClassPropertyI(c,p,i)    { epregisterClassName2<c>(#c); epregisterClassProperty2<c>(&c::p,#p,i); }

#define epregisterClassInheritance(c,pc) { epregisterClassName2<c>(#c); epregisterClassName2<pc>(#pc); epregisterClassInheritance2<c,pc>(); epregisterClassConverterDynamicCast(pc,c); }
#define epregisterClassPolymorphic(c,pc) { epregisterClassName2<c>(#c); epregisterClassName2<pc>(#pc); epregisterClassInheritance2<c,pc>(); epregisterClassConverterDynamicCast(pc,c); epregisterClassConverterDynamicCast(c,pc); }

#define epregisterClassCout(c) { epregisterClassName2<c>(#c); epregisterClassCout2<c>(&eclassCout<c>); }

#define epregisterClass(c) { epregisterClassName2<c>(#c); epregisterClassConstructor(c,()); epregisterClassConstructor(c,(const c&)); epregisterClassAssign(c); epregisterClassCopy(c); }

#define epregisterClassAssign(c) { epregisterClassName2<c>(#c); epregisterClassAssign2<c>(&eclassAssign<c>); }

#define epregisterClassCopy(c)             { epregisterClassName2<c>(#c); epregisterClassCopy2<c>(&eclassCopy<c>); }
#define epregisterClassJSONSerialMethod(c) { epregisterClassName2<c>(#c); epregisterClassJSONSerial2<c>(&eclassJSONSerialMethod<c>); }
#define epregisterClassJSONSerialType(c)   { epregisterClassName2<c>(#c); epregisterClassJSONSerial2<c>(&eclassJSONSerialType<c>); }
#define epregisterClassSerialMethod(c)     { epregisterClassName2<c>(#c); epregisterClassSerial2<c>(&eclassSerialMethod<c>); }
#define epregisterClassSerialType(c)       { epregisterClassName2<c>(#c); epregisterClassSerial2<c>(&eclassSerialType<c>); }
#define epregisterClassUnserialMethod(c)   { epregisterClassName2<c>(#c); epregisterClassUnserial2<c>(&eclassUnserialMethod<c>); }
#define epregisterClassUnserialType(c)     { epregisterClassName2<c>(#c); epregisterClassUnserial2<c>(&eclassUnserialType<c>); }
#define epregisterClassSerialfMethod(c)     { epregisterClassName2<c>(#c); epregisterClassSerialf2<c>(&eclassSerialfMethod<c>); }
#define epregisterClassSerialfType(c)       { epregisterClassName2<c>(#c); epregisterClassSerialf2<c>(&eclassSerialfType<c>); }
#define epregisterClassUnserialfMethod(c)   { epregisterClassName2<c>(#c); epregisterClassUnserialf2<c>(&eclassUnserialfMethod<c>); }
#define epregisterClassUnserialfType(c)     { epregisterClassName2<c>(#c); epregisterClassUnserialf2<c>(&eclassUnserialfType<c>); }


#define epregisterClassJSONSerializeType(c)   { epregisterClassName2<c>(#c); epregisterClassJSONSerialType(c); }
#define epregisterClassJSONSerializeMethod(c) { epregisterClassName2<c>(#c); epregisterClassJSONSerialMethod(c); }

#define epregisterClassSerializeType(c)   { epregisterClassName2<c>(#c); epregisterClassSerialType(c); epregisterClassUnserialType(c); epregisterClassSerialfType(c); epregisterClassUnserialfType(c); }
#define epregisterClassSerializeMethod(c) { epregisterClassName2<c>(#c); epregisterClassSerialMethod(c); epregisterClassUnserialMethod(c); epregisterClassSerialfMethod(c); epregisterClassUnserialfMethod(c); }


#endif

