#ifndef EVARCLASSMETHOD_H
#define EVARCLASSMETHOD_H

#include "eutils.h"

#include "evar_dec.h"
#include "ebasicarray.h"
#include "evararray.h"
#include "estrarray.h"

#undef check

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

class eclassMethodBase
{
 public:
  const type_info* fReturn;
  ebasicarray<const type_info*> fArgs;
  evararray defargs;

  void setDefaultArgs(evararray& args);

  virtual const type_info& getTypeid()=0;
  virtual evar operator()(evarBase* obj,const evararray& args)=0;

  eclassMethodBase();
  eclassMethodBase(const evararray& args);
};

ostream& operator<<(ostream& stream,const eclassMethodBase& method);

template <class T,class F>
class eclassMethod : public eclassMethodBase
{
 public:
  F method;
  eclassMethod(F method,const estr& methodname,const evararray& args,const estr& info="");
  evar operator()(evarBase* obj,const evararray& args);

  virtual const type_info& getTypeid() { return(typeid(F)); }

  void updateInfo();
};

/*
class eboundBase
{
 public:
  virtual bool check(const evar& f)=0;
};

//class eboundFunction 

class ebounds : public ebasicarray<eboundBase>
{
 public:
};

template <class T,class F>
class eclassMethodBounds : public eclassMethodBase
{
 public:
  F method;
  ebounds bounds;

  eclassMethodBounds(F method,const estr& methodname,const evararray& args,const ebounds& bounds,const estr& info="");
  evar operator()(evarBase* obj,const evararray& args);

  virtual const type_info& getTypeid() { return(typeid(F)); }

  void updateInfo();
};
*/

#include "efunc.h"

template <class T,class F>
class eclassMethodFunc : public eclassMethodBase
{
 public:
  efunc func;
  eclassMethodFunc(F func,const estr& methodname,const evararray& args,const estr& info="");
  evar operator()(evarBase* obj,const evararray& args);

  virtual const type_info& getTypeid() {return(typeid(F));}

  void updateInfo();
};

#include "estrarray.h"
#include "evarmethodcalls.h"
#include "evarmethodinfo.h"

template <class T,class F>
eclassMethod<T,F>::eclassMethod(F _method,const estr& methodname,const evararray& args,const estr& info): eclassMethodBase(args),method(_method)
{
  updateInfo();
}

template <class T,class F>
void eclassMethod<T,F>::updateInfo()
{
  eclassMethodInfo(method,*(eclassMethodBase*)this);
}

template <class T,class F>
evar eclassMethod<T,F>::operator()(evarBase* obj,const evararray& args)
{
  evararray _args(args);
  eclassMethodBase::setDefaultArgs(_args);
  if (obj->getTypeid()==typeid(T))
    return(eclassMethodCall(dynamic_cast<evarType<T>*>(obj)->object,method,_args));
  else{
    evar tmpv(evar(obj).convert(typeid(T)));
//    tmpv.convert(typeid(T));
    if (tmpv.isNull()) return(tmpv);
    return(eclassMethodCall(dynamic_cast<evarType<T>*>(tmpv.var)->object,method,_args));
  }
}

/*
template <class T,class F>
eclassMethodBounds<T,F>::eclassMethodBounds(F _method,const estr& methodname,const evararray& args,const ebounds& _bounds,const estr& info): eclassMethodBase(args),method(_method),bounds(_bounds)
{
  updateInfo();
}

template <class T,class F>
void eclassMethodBounds<T,F>::updateInfo()
{
  eclassMethodInfo(method,*(eclassMethodBase*)this);
}

template <class T,class F>
evar eclassMethodBounds<T,F>::operator()(evarBase* obj,const evararray& args)
{
  evararray _args(args);
  eclassMethodBase::setDefaultArgs(_args);
  return(eclassMethodCall(dynamic_cast<evarType<T>*>(obj)->object,method,_args));
}
*/



template <class T,class F>
eclassMethodFunc<T,F>::eclassMethodFunc(F _func,const estr& methodname,const evararray& args,const estr& info): eclassMethodBase(args),func(_func)
{
  updateInfo();
}

template <class T,class F>
void eclassMethodFunc<T,F>::updateInfo()
{
  int i;
  for (i=1; i<func.fArgs.size(); ++i)
    eclassMethodBase::fArgs.add(func.fArgs[i]);
  eclassMethodBase::fReturn = func.fReturn;
}

template <class T,class F>
evar eclassMethodFunc<T,F>::operator()(evarBase* obj,const evararray& args)
{
  evararray _args;
  _args.add(obj);
//  _args.add(dynamic_cast<evarType<T>*>(obj)->object);
  _args+=args;
  return(func.call2(_args));
//  return(func.call2(args));  // eparserinterpreter::objop should put the object as the first element immediately
}

#endif

