#ifndef EPARSER_H
#define EPARSER_H

#include "eutils.h"
#include "evar_dec.h"


#include "estrhash.h"
#include "estrhashof.h"
#include "evararray.h"
#include "evarhash.h"
#include "einthashof.h"


class estr;
class evar;
class efunc;
class eparser;
class emodule;

#define epregister( obj )  epregister2(obj, #obj)
#define epregisterI( obj , info )  epregister2(obj, #obj,info)
template <class T>
void epregister2(T* object,const estr& objname,const estr& info="");

#define eregisterOption(options,var) eregisterOption2(options,var,#var)
template <class T>
void eregisterOption2(const evarhash& options,T& var,const estr& objname);

void epregisterAction(const estr& name,const efunc& f);

#define epregisterModuleFunc(m, func )  epregisterModuleFunc3(m,func,#func)

#define epregisterFunc( func )  epregisterFunc3(func,#func)
#define epregisterFuncD( func , defargs )  epregisterFunc3(func,#func,defargs)
#define epregisterFunc2( func , ret , args )  epregisterFunc3((ret (*)args)&func,#func)
#define epregisterFunc2D( func , defargs, ret , args )  epregisterFunc3((ret (*)args)&func,#func,defargs)
template <class F>
void epregisterFunc3(F func,const estr& funcname,const evararray& args=evararray());

void epregisterModuleFunc3(emodule& m,const efunc& func,const estr& funcname);

#include "efunc_dec.h"

class eparser
{
 public:
  int argvc;
  char **argv;
  earray<estr> args;

  estrhash   initvalues;
  estrhash   argAlias;

  estr programName;
  estr programVersion;
  estr programShort;
  estr programExamples;
  estr programAuthors;
  estr programUsage;
  estr programMisc;

  estrhashof<efunc> actions;
  estrhash          actionInfo;
  estrhashof<evar>  objects;
  estrhash          objectInfo;

  einthashof<evar>  tmpVars;

  estrhashof< earray<efunc> > funcs;
//  estrhashof<epclassBase> classes;

  bool parsing;
  bool showRegister;

  bool initiated;

  bool showHelp;
  bool userRegister;
  efunc onHelp;

  eparser();
  ~eparser();

  void registerParser();
  void registerEnd();

  void help(const estr& line="");

  void save(const estr& filename);
  void load(const estr& filename);

  void execArg(estr argname,const estr& values);
  void parseArgs(int _argvc,char* _argv);
  void parseArgs();
};

//extern eparser* parser;
eparser& getParser();
void setParser(eparser& parser);

#include "estr.h"

#include "earray.h"
#include "logger.h"
#include "evar.h"
#include "efunc.h"
#include "evararray.h"
#include "evarclass.h"
#include "emodule.h"

//#include "eparserinterpreter.h"

// epregisterClassHeader(eparser);
// epregisterClassHeader(elogger);

bool epvarExists(const estr& varname);
bool epfuncExists(const estr& funcname);

/*
void epset(const estr& objname,const evar& value);
evar epget(const estr& objname);
evar epcall(const estr& funcname,const estr& args="");

void epset(const estr& objname,const estr& propertyname,const evar& value);
evar epget(const estr& objname,const estr& propertyname);
evar epcall(const estr& objname,const estr& methodname,const estr& args);
*/


template <class T>
void eregisterOption2(const evarhash& vhash,T& val,const estr& objname)
{
  if (vhash.exists(objname)){
    val=vhash.values(objname);
  }
}

template <class T>
void epregister2(T* object,const estr& objname,const estr& info)
{
  ldinfo("registering object: "+objname);
  
  getParser().objects.add(objname,new evar(object));
  if (info.len())
    getParser().objectInfo.add(objname,info);

/*
  // At the moment of registering a variable, assign it the value
  // found in "initvalues"
  if (parser->initvalues.exists(objname)){
    ldinfo("setting initial value: "+objname);
    epset(objname,parser->initvalues[objname]);
  }

  // if help command was called during parsing, we show any new registered variables
  if (parser->showRegister){
    cout << "  --"<<objname<<"   "<<info<<endl;
  }

  if (getClasses().exists(typeid(T).name())){
    eclass<T> *c;
    GETCLASS(c,T);
    int i;
    ldinfo("object has registered class: "+objname);
    for (i=0; i<c->methods.size(); ++i){
      if (parser->initvalues.exists(objname+"."+c->methods.keys(i)))
        epcall(objname,c->methods.keys(i),parser->initvalues[objname+"."+c->methods.keys(i)]);
    }
    for (i=0; i<c->properties.size(); ++i){
      ldinfo("searching properties: "+c->properties.keys(i));
      if (parser->initvalues.exists(objname+"."+c->properties.keys(i)))
        epset(objname,c->properties.keys(i),parser->initvalues[objname+"."+c->properties.keys(i)]);
    }
  }
*/
}

template <class T>
void epregister2(T& object,const estr& objname,const estr& info="")
{
//  epclassAssignId<T>();
  ldinfo("registering object: "+objname);

  getParser().objects.add(objname,new evar(&object));
  if (info.len())
    getParser().objectInfo.add(objname,info);

/*
  // At the moment of registering a variable, assign it the value
  // found in "initvalues"
  if (parser->initvalues.exists(objname))
    epset(objname,parser->initvalues[objname]);

  if (parser->showRegister)
    cout << "  --"<<objname<<"    "<<info<<endl;

  if (parser->classes.exists(typeid(T).name())){
    epclass<T> *c;
    GETCLASS(c,T);
    int i;
    for (i=0; i<c->methods.size(); ++i){
      if (parser->initvalues.exists(objname+"."+c->methods.keys(i)))
        epcall(objname,c->methods.keys(i),parser->initvalues[objname+"."+c->methods.keys(i)]);
    }
    for (i=0; i<c->properties.size(); ++i){
      if (parser->initvalues.exists(objname+"."+c->properties.keys(i)))
        epset(objname,c->properties.keys(i),parser->initvalues[objname+"."+c->properties.keys(i)]);
    }
  }
*/
}


template <class F>
void epregisterFunc3(F func,const estr& funcname,const evararray& args)
{
  if (!getParser().funcs.exists(funcname))
    getParser().funcs.add(funcname,earray<efunc>());

  getParser().funcs[funcname].add(efunc(func,args));

  // At the moment of registering a function, call it with the value
  // found in "initvalues"
  
//  if (parser->initvalues.exists(funcname))
//    epcall(funcname,parser->initvalues[funcname]);
}

void epregisterAlias(const estr& alias,const estr& var);
void epregisterEnd();

void eparseArgs();
void eparseArgs(int& _argvc,char** &_argv);

evar apply(const efunc& func,const evararray& args);

evar maxvar(const evar& v1,const evar& v2);
evar minvar(const evar& v1,const evar& v2);
evararray divarr(evararray& farr,float mval);
evararray subarr(evararray& farr,float mval);
evararray sumarr(const evararray& farr,float mval);
evararray invarr(const evararray& farr);
evararray mularr(const evararray& farr,float mval);
evararray minarr(const evararray& farr,float mval);
evararray maxarr(const evararray& farr,float mval);
evararray logarr(const evararray& farr);

evararray mularr(const evararray& farr,const evararray& farr2);
evararray sumarr(evararray& farr,evararray& farr2);
evararray subarr(evararray& farr,evararray& farr2);
evararray divarr(evararray& farr,evararray& farr2);


/*
template <class P>
void epset(const estr& objname,const estr& propertyname,P value)
{
  if (parser.objects.exists(objname)){
    parser.objects[objname]->set(propertyname,value);
    return;
  }
  lwarn("parser: object \""+objname+"\" not found, while setting property: "+propertyname);
}
*/


#endif

