#ifndef EVAR_DEC_H
#define EVAR_DEC_H

#include <functional>
#include <typeinfo>
#include <iostream>
using namespace std;


class estr;
class efile;
class evarBase;
class eclassMethodBase;

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


class evar
{
 public:
  evarBase* var;

  evar();
  evar(evarBase* value);
  evar(evar* value);
  evar(const evar* value);
  evar(const evar& value);
  evar(evar& value);

  evar(const char* value);

/*
  evar(double& value);
  evar(float& value);
  evar(unsigned char& value);
  evar(unsigned int& value);
  evar(unsigned long int& value);
  evar(int& value);
  evar(long int& value);
  evar(char& value);
  evar(bool& value);

  evar(const double& value);
  evar(const float& value);
  evar(const unsigned char& value);
  evar(const unsigned int& value);
  evar(const unsigned long int& value);
  evar(const char& value);
  evar(const int& value);
  evar(const long int& value);
  evar(const bool& value);
*/

  template <typename T>
  evar(T* value);
  template <typename T>
  evar(const T* value);

  template <typename T>
  evar(T& value);
  template <typename T>
  evar(const T& value);

  evar(void ());

#if __clang_major__ >= 5
//  template <typename T>
//  evar(function<T> f); // removed because it does not allow evar to be converted to function pointers when calling methods or functions
  template <typename R, typename ...Args>
  evar(R(*f)(Args...));
#else
  template <typename T1>
  evar(void (T1));
  template <typename T1,typename T2>
  evar(void (T1,T2));
  template <typename T1,typename T2,typename T3>
  evar(void (T1,T2,T3));
  template <typename T1,typename T2,typename T3,typename T4>
  evar(void (T1,T2,T3,T4));
  template <typename T1,typename T2,typename T3,typename T4,typename T5>
  evar(void (T1,T2,T3,T4,T5));
  template <typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>
  evar(void (T1,T2,T3,T4,T5,T6));

  template <typename R>
  evar(R ());
  template <typename R,typename T1>
  evar(R (T1));
  template <typename R,typename T1,typename T2>
  evar(R (T1,T2));
  template <typename R,typename T1,typename T2,typename T3>
  evar(R (T1,T2,T3));
  template <typename R,typename T1,typename T2,typename T3,typename T4>
  evar(R (T1,T2,T3,T4));
  template <typename R,typename T1,typename T2,typename T3,typename T4,typename T5>
  evar(R (T1,T2,T3,T4,T5));
  template <typename R,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>
  evar(R (T1,T2,T3,T4,T5,T6));
#endif

  ~evar();
  void clear();

  const char *getClass() const;
  bool isTypeid(const type_info&) const;
  const type_info& getTypeid() const;
  const type_info& getPTypeid() const;

  bool isNull() const;

  void create(const estr& type);
  void json_serial(estr& data) const;
  void serial(estr& data) const;
  size_t unserial(const estr& data,size_t i=0);
  void serial(const efile& f) const;
  bool unserial(const efile& f);

  evar& get() const;
  evar* getarg(void (*pfunc)(evar*)) const;
  evar& getarg(void (*pfunc)(evar)) const;
  const evar& getarg(void (*pfunc)(const evar&)) const;
  evar& getarg(void (*pfunc)(evar&)) const;
//  evar& getarg(void (*pfunc)(evar&&)) const;

  template <typename T>
  T& get() const;
  template <typename T>
  T* getarg(void (*pfunc)(T*)) const;
  template <typename T>
  T& getarg(void (*pfunc)(T)) const;
  template <typename T>
  const T& getarg(void (*pfunc)(const T&)) const;
  template <typename T>
  T& getarg(void (*pfunc)(T&)) const;
//  template <typename T>
//  T& getarg(void (*pfunc)(T&&)) const;


//  template <class T>
//  evar convert() const;
  evar convert(const type_info& ti) const;

//  template <class T>
//  bool isConvertible() const;

  bool isConvertible(const type_info& ti) const;

/*
  template <class T>
  evar& operator=(T* value);
  template <class T>
  evar& operator=(const T& value);
*/

  void retain();
  void release();

  evar copyPtr() const;
  evar copy() const;
  evar& set(const evar& value);
  evar& copy(const evar& value);
  evar& operator=(evarBase* value);
  evar& operator=(const evar& value);
  evar& operator=(const char* value);

  earray<estr> properties() const;
  earray<estr> methods() const;

  bool hasProperty(const estr& pname);
  bool hasMethod(const estr& mname);

  evar  property(const estr& pname) const;
  eclassMethodBase* getCallMethod(const estr& mname,const evararray& args) const;
  evar  call(const estr& mname,const evararray& args) const;
  void  call(evar& rvar,const estr& mname,const evararray& args) const;


  evar operator()(const evararray& args);

  evar& operator-=(const evar& value);
  evar& operator+=(const evar& value);

  evar  operator+(const evar& value);
  evar  operator-(const evar& value);
  evar  operator*(const evar& value);
  evar  operator/(const evar& value);
  evar  operator%(const evar& value);

  evar  operator+(const evar& value) const;
  evar  operator-(const evar& value) const;
  evar  operator*(const evar& value) const;
  evar  operator/(const evar& value) const;
  evar  operator%(const evar& value) const;

  evar&  operator++();
  evar&  operator--();

  evar  operator[](const evar& value);

  bool  operator==(const evar& value) const;
  inline bool operator!=(const evar& value) const { return(!(*this==value)); }

  bool  operator<(const evar& value) const;
  bool  operator<=(const evar& value) const;
  bool  operator>(const evar& value) const;
  bool  operator>=(const evar& value) const;

  template <typename T>
  operator T() const;


/*
  template <class T>
  void setref(T& value);

  inline void set(const evar& value);

  inline evar call(const estr& method,evararray& args);
  inline evar get(const estr& property) const;
  inline void set(const estr& property,const evar& value);

  evar call(const estr& method,const estr& args="");
*/
};

void evarSplitArgs(evararray &arr,const estr& args);
ostream& operator<<(ostream& stream,const evar& variable);

template <typename T>
evar evarCopy(const T& obj);
//template <typename T>
//evar evarCopy(T* const obj);
template <typename T>
evar evarRef(T* obj);
template <typename T>
evar evarRef(T& obj);

template <>
evar evarCopy(const evar& obj);

template <>
evar evarRef(evar& obj);
template <>
evar evarRef(evar* obj);

template <>
evar evarRef(void* obj);

void json_serialbool(bool v,estr &data);
void serialbool(bool v,estr &data);
size_t unserialbool(bool& v,const estr& data,size_t i);
void serialbool(bool v,const efile& f);
bool unserialbool(bool& v,const efile& f);

void json_serialint(int v,estr &data);
void serialint(int v,estr &data);
size_t unserialint(int& v,const estr& data,size_t i);
void serialint(int v,const efile& f);
bool unserialint(int& v,const efile& f);

void json_serialuint(unsigned int v,estr &data);
void serialuint(unsigned int v,estr &data);
size_t unserialuint(unsigned int& v,const estr& data,size_t i);
void serialuint(unsigned int v,const efile& f);
bool unserialuint(unsigned int& v,const efile& f);

void json_serialfloat(float v,estr &data);
void serialfloat(float v,estr &data);
size_t unserialfloat(float& v,const estr& data,size_t i);
void serialfloat(float v,const efile& f);
bool unserialfloat(float& v,const efile& f);

void json_serialdouble(double v,estr &data);
void serialdouble(double v,estr &data);
size_t unserialdouble(double& v,const estr& data,size_t i);
void serialdouble(double v,const efile& f);
bool unserialdouble(double& v,const efile& f);

void json_seriallong(long v,estr &data);
void seriallong(long v,estr &data);
size_t unseriallong(long& v,const estr& data,size_t i);
void seriallong(long v,const efile& f);
bool unseriallong(long& v,const efile& f);

void json_serialulong(unsigned long v,estr &data);
void serialulong(unsigned long v,estr &data);
size_t unserialulong(unsigned long& v,const estr& data,size_t i);
void serialulong(unsigned long v,const efile& f);
bool unserialulong(unsigned long& v,const efile& f);


void json_serial(const evar& var,estr& data);
void serial(const evar& var,estr& data);
evar unserial(const estr& data);

template <typename T>
void json_serial(const T& var,estr& data);
template <typename T>
void serial(const T& var,estr& data);

template <typename T>
size_t unserial(T& var,const estr& data,size_t i);

estr getClassName(const type_info* tinfo);

#endif

