#include "evar.h"
#include "evarclass.h"
#include "eparser.h"
#include "estr.h"
#include "estrarray.h"
#include "efile.h"
#include "emain.h"
#include "eparserinterpreter.h"
#include "edistcomp.h"
#include "estrhashof.h"
#include "elongarray.h"

#include "ehttpHub.h"
#include "ehttp.h"
#include "ehtml.h"
#include "evarcommon.h"

#ifndef _WIN32
 #include <unistd.h>
 #include <pwd.h>
#else
 #include <io.h>
 #define WEBHTMLPATH "C:\\Program Files\\eutils\\webhtml"
 #define read _read
#endif

class ehttpParser;

class ehttpParserConnection : public ehttpHubConnection
{
 public:
  ehttpParserConnection(ehttpParser& server);
  virtual void doSendStatus(esvararray& arr);
 
  bool validateLogin(const estr& user,const estr& pass);
  void handleLogin(esvararray& msg);
  void handleInterpret(esvararray& msg);
  void handleCommand(esvararray& msg);
};

class ehttpParser : public ehttpHub
{
 public:
  void doIncoming();
};


ehttpParserConnection::ehttpParserConnection(ehttpParser& _server): ehttpHubConnection(static_cast<ehttpHub&>(_server)) {}

estr results;

bool ehttpParserConnection::validateLogin(const estr& user,const estr& pass)
{
  return(true);
}

void ehttpParserConnection::handleLogin(esvararray& msg)
{
  lerrorifr(msg.findkey("user")==-1 || msg["user"].getTypeid()!=typeid(estr),"Command sent but no \"user\" field or it is not a string",);
  lerrorifr(msg.findkey("pass")==-1 || msg["pass"].getTypeid()!=typeid(estr),"Command sent but no \"pass\" field or it is not a string",);
  estr& user(msg["user"].get<estr>());
  estr& pass(msg["pass"].get<estr>());
  lerrorifr(user.len()==0,"Empty user field",);
  
  if (validateLogin(user,pass)){
    linfo("session: "+session+" succesfully validated user: "+user);
    server.client[session].username=user;
    estr data="{\"type\":\"login\",\"login\":1}";
    server.client[session].send(data);
    return;
  }
  estr data="{\"login\":0}";
  server.client[session].send(data);
  lwarn("session: "+session+" wrong user/pass: "+user);
}

void ehttpParserConnection::handleInterpret(esvararray& msg)
{
  lerrorifr(msg.findkey("cmd")==-1 || msg["cmd"].getTypeid()!=typeid(estr),"Command sent but no \"cmd\" field or it is not a string",);
  estr& cmd(msg["cmd"].get<estr>());

  cout << ">" << cmd << endl;
  evar res(epinterpret(cmd));
  
  if (res.isNull()) return;

  estr data;
  data="{\"uid\":";
  data+=session;
  data+=",\"type\":\"result\",\"cmd\":";
  cmd.json_serial(data);
  data+=",\"result\":";
  res.json_serial(data);
  data+="}";
  
  server.sendAll(data,"0");
}

void ehttpParserConnection::handleCommand(esvararray& arr)
{
  lerrorifr(arr.findkey("type")==-1 || arr["type"].getTypeid()!=typeid(estr),"command from client does not have type or it is not a string: ",);

  estr& type(arr["type"].get<estr>());

  if (type=="msg")
    handleGlobalMsg(arr);
  else if (type=="cmd")
    handleInterpret(arr);
  else if (type=="login")
    handleLogin(arr);
  else
    lerror("unknwown message type: "+type+" in message");
}

void ehttpParserConnection::doSendStatus(esvararray& arr)
{
  esvararray *acdbWords=new esvararray;
  for (int i=0; i<getParser().objects.size(); ++i)
    acdbWords->add(acdbWords->size(),new estr(getParser().objects.keys(i)));
  for (int i=0; i<getParser().funcs.size(); ++i)
    acdbWords->add(acdbWords->size(),new estr(getParser().funcs.keys(i)));
  for (int i=0; i<getClassNames().size(); ++i)
    acdbWords->add(acdbWords->size(),new estr(getClassNames().keys(i)));
  acdbWords->add("length",new estr(acdbWords->size()));
  arr.add("acdbWords",acdbWords);
}




void ehttpParser::doIncoming()
{
  ehttpParserConnection *conn = new ehttpParserConnection(*this);
  sockets.addref(conn);
  lwarnif(!accept(*conn),"failure in accepting connection");
}

ehttpParser httpServer;

int outpipe;

void doOutput()
{
  int len;
  estr output;
  output.reserve(1024);
  do{
    len=read(outpipe,&output._str[output.len()],1024);
    output._strlen+=len;
    output.reserve(output.len()+1024);
  } while (len==1024);
  ldieif(len==-1,"error reading from outpipe");
  results+=output;

  esvararray outputmsg;
  outputmsg.add("type",new estr("output"));
  outputmsg.add("output",new estr(output));

  httpServer.sendAll(json2_serialize(outputmsg),"0");
}

void startWebServer()
{
/*
  int pipefd[2];
  pipe(pipefd);
  dup2(pipefd[1],1);
  outpipe=pipefd[0];

  getSystem().addReadCallback(outpipe,doOutput,evararray());
*/
  httpServer.wd=WEBHTMLPATH;

  if (!httpServer.listen(19888,"0.0.0.0"))
    ldie("unable to bind to port");

  epregisterFunctions();
//  getSystem().run();
}



void interpretscript(const estr& filename)
{
  efile file(filename);
  epinterpret(file.data());
}

void varprintm(const evararray& arr)
{
  int i;
  for (i=0; i<arr.size()-1; ++i){
    cout << arr[i] << " ";
  }
  if (i<arr.size())
    cout << arr[arr.size()-1] << endl;
}

void doAccept(esocket& server)
{
  esocket s;
  while (server.accept(s))
    getDistComp().startTCPClient(s);
} 

esocket server;

void startDCServer()
{
  server.onIncoming=doAccept;
  server.listen(19988);
}

#include <sys/types.h>
#include <sys/stat.h>

//#include <uuid/uuid.h>

#ifndef _WIN32
#include "edaemon.h"
#endif

/*
estr optInterpret;

void startInterpret()
{
  if (optInterpret.len()){
    epregisterFunctions();
    epinterpret(optInterpret);
  }

  if (getParser().args.size()>1){
    getParser().args.erase(0); // remove esh argument, keep only script name
    epinterpretfile(getParser().args[0]);
  } else
    setupInterpret();
}
*/

int emain()
{
  getParser().programName="esh - easy shell";
  getParser().programShort="Easy C++ based shell for collaborative and high performance distributed computing";
  getParser().programVersion="1.0";
  getParser().programAuthors="Joao F. Matias Rodrigues <jfmrod@konceptfx.com>";
  getParser().programUsage="[-h] [script]";

  estrarray m; // modules to be loaded
  epregisterI(m,"comma separated list of modules to be loaded");
  epregister2(getParser().args,"args");
  bool webserver=false;
  epregisterI(webserver,"start webserver");
  bool dcnode=false;
  epregisterI(dcnode,"start computing node");
  bool dcserver=false;
  epregisterI(dcserver,"start TCP distributed computing server");
  estr dchost;
  epregisterI(dchost,"connect via TCP to distributed computing host");
  estr dcgroup;
  epregisterI(dcgroup,"distributed computing group to join");
/*
  bool daemon=false;
  epregisterI(daemon,"start daemon");
  bool dconnect=false;
  epregisterI(dconnect,"connect to local daemon");
  bool dbridge=false;
  epregisterI(dbridge,"bridge connection to local daemon");
  estr sfile;
  epregisterI(sfile,"socket file to connect with daemon");
*/

  epregisterFunc3(varprintm,"printm");

  epregisterFunc(interpretscript);

#ifndef _WIN32
  daemonArgs();
#endif

  eparseArgs();
  ldieif(dcgroup.len() && !dchost.len(),"must set dcserver hostname with -dchost");

  for (int i=0; i<m.size(); ++i){
    if (!getModules().load(m[i]))
      lerror("loading module: "+m[i]);
  }

  epregisterFunc(startWebServer);
  epregisterFunc(startDCServer);

/*
  if (daemon){
    startDaemon(sfile);
    exit(0);
  }
  if (dconnect){
    startDaemonConnect(sfile);
    exit(0);
  }
  if (dbridge){
    startDaemonBridge(sfile);
    exit(0);
  }
*/
  if (webserver)
    startWebServer();
  if (dcserver)
    startDCServer();

  if (dcnode)
    startDistCompLocalNode();
  else if (dchost.len())
    startDistCompRemoteNode(dchost,dcgroup);
 
#ifndef _WIN32
  daemonConnectInitDefault();
#else
  setupInterpret();
#endif

  getSystem().run();

//  if (efile(getParser().args[0]).basename()=="esh" && getParser().args.size()>1){
  return(0);
}
