#include "piacmd.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

#include "basexecut.h"

#include "pdlmgr.h"
#include "ctimer.h"
// #include "dlftypes.h"

#include "pistdimgapp.h"
#include "nobjmgr.h"
#include "piafitting.h"
#include "pawexecut.h"
#include "cxxexecutor.h"
#include "cxxexecwin.h"

#include PISTDWDG_H 
#include PILIST_H 

// ------------------------------------------------------------
//         Gestion d'une fenetre d'aide interactive  
//                    Classe   PIAHelpWind   
// ------------------------------------------------------------

class PIAHelpWind : public PIWindow {
public :
		PIAHelpWind(PIStdImgApp* par, PIACmd* piacmd);
  virtual	~PIAHelpWind();
  virtual void  Show();
  virtual void  Process(PIMessage msg, PIMsgHandler* sender, void* data=NULL);
  inline  void  AddHelpGroup(const char * hgrp, int gid)
                    { hgrpom->AppendItem(hgrp, 20000+gid); }
  inline  void  ClearHelpList() 
                    { mNitem=0; hitemlist->DeleteAllItems(); }
  inline  void	AddHelpItem(const char * hitem) 
		    { mNitem++;  hitemlist->AppendItem(hitem, 100+mNitem); }
protected :
  PIStdImgApp* dap;
  PIACmd* piac;
  int mNitem;
  PIList* hitemlist;
  PIOptMenu* hgrpom;
  PIButton * mBut;
  PILabel * mLab;
  PIText* mTxt;
};

/* --Methode-- */
PIAHelpWind::PIAHelpWind(PIStdImgApp *par, PIACmd* piacmd)
  : PIWindow((PIMsgHandler *)par, "Help-PIApp", PIWK_normal,  400, 300, 100, 350)
{
dap = par;
piac = piacmd;
mNitem = 0;
SetMsg(77);

int bsx, bsy;
int tsx, tsy;
int spx, spy;
PIApplicationPrefCompSize(bsx, bsy);
spx = bsx/6;   spy = bsy/6;
tsx = 10*bsx+2*spx;  tsy = 7*bsy+3*spy;
SetSize(tsx,tsy);
hgrpom = new PIOptMenu(this, "hgrpoptmen", bsx*2.0, bsy, spx/2, spy);
hgrpom->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
hitemlist = new PIList(this, "hitemlist", bsx*2.0, tsy-3*spy-bsy, spx/2, 2*spy+bsy);
hitemlist->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
// hitemlist->SetBorderWidth(2);
mTxt = new PIText(this, "helptext", true, true, bsx*8.0, 6*bsy, bsx*2.0+1.5*spx, spy);
// mTxt->SetMutiLineMode(true);
mTxt->SetTextEditable(false);
mTxt->SetText("");
mTxt->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
mLab = new PILabel(this, "helpitem", bsx*4, bsy, bsx*2.5+2*spx, tsy-spy-bsy);
mLab->SetBorderWidth(1);
mLab->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
mLab->SetLabel("");
mBut = new PIButton(this, "Close", 70, bsx, bsy, tsx-bsx*1.5-spx, tsy-spy-bsy);
mBut->SetBinding(PIBK_elastic,PIBK_elastic, PIBK_elastic,PIBK_elastic);
}

/* --Methode-- */
PIAHelpWind::~PIAHelpWind()
{
delete hgrpom;
delete hitemlist;
delete mTxt;
delete mLab;
delete mBut;
}

/* --Methode-- */
void PIAHelpWind::Process(PIMessage msg, PIMsgHandler* sender, void* /*data*/)
{
PIMessage um = UserMsg(msg);
if (((um == 77) && (ModMsg(msg) == PIMsg_Close)) || (um == 70) ) {
  Hide();
  return;
  }
else if ( (um >= 20000) && (sender == hgrpom)) {  // Selection de groupe de Help
  mTxt->SetText("");
  mLab->SetLabel("");
  piac->UpdateHelpList(this, um-20000);
}
else if ( (um > 100) && (sender == hitemlist) && (ModMsg(msg) == PIMsg_Select) ) {
  string s = hitemlist->GetSelectionStr();
  mTxt->SetText(piac->GetUsage(s));
  mLab->SetLabel(s);
  }  
}

/* --Methode-- */
void PIAHelpWind::Show()
{
hgrpom->SetValue(20000);   // Groupe All
mTxt->SetText("");
mLab->SetLabel("");
piac->UpdateHelpList(this, 0); 
PIWindow::Show();
}

// ------------------------------------------------------------
//         Bloc de commandes (Foreach, ...)    
//               Classe  PIACmdBloc    
// ------------------------------------------------------------

class PIACmdBloc {
public:
  enum BType { BT_None, BT_ForeachList, BT_ForeachInt, BT_ForeachFloat };

  PIACmdBloc(PIACmd* piac, PIACmdBloc* par, string& kw, vector<string>& args); 
  ~PIACmdBloc();
  inline PIACmdBloc*   Parent() { return(parent); }
  inline bool   CheckOK() { return blkok; }
  inline void	AddLine(string& line)  
    { lines.push_back(line); bloclineid.push_back(lines.size()); }
  inline void	AddBloc(PIACmdBloc* blk)  
    { blocs.push_back(blk); bloclineid.push_back(-blocs.size()); }
  PIACmdBloc*	Execute();
protected:
  PIACmd* piacmd;
  PIACmdBloc* parent;
  bool blkok;        // true -> block OK
  BType typ;         // foreach , integer loop, float loop, test 
  string varname;
  vector<string> strlist;
  vector<string> lines;
  vector<PIACmdBloc *> blocs;
  vector<int> bloclineid;
  int i1,i2,di;
  float f1,f2,df;
};

/* --Methode-- */
PIACmdBloc::PIACmdBloc(PIACmd* piac, PIACmdBloc* par, string& kw, vector<string>& args)
{
piacmd = piac;
parent = par;
blkok = false;
typ = BT_None;
i1 = 0;  i2 = -1;  di = 1;
f1 = 0.; f2 = -1.; df = 1.;
if ((args.size() < 2) ||  !isalpha((int)args[0][0]) )  return; 
if ((kw != "foreach") && (kw != "for"))  return;
varname = args[0];  // $CHECK$ Variable name should be checked 
//if (isalpha((int)args[1][0]) ) {  This is a foreach bloc with string list
if (kw == "foreach" ) { // This is a foreach bloc with string list
  for(int kk=1; kk<args.size(); kk++) strlist.push_back(args[kk]);
  typ = BT_ForeachList;
  blkok = true;
  }
else { // This is an integer or float loop
  size_t l = args[1].length();
  size_t p = args[1].find(':');
  size_t pp = args[1].find('.');
  bool fl = (pp < l) ? true : false;  // Float loop or integer loop
  if (p >= l) return;  // Syntaxe error
  string a1 = args[1].substr(0, p);
  string aa = args[1].substr(p+1);
  p = aa.find(':');
  string a2, a3;
  bool hasa3 = false;
  if (p < aa.length() ) { 
    a2 = aa.substr(0,p);
    a3 = aa.substr(p+1);
    hasa3 = true;
    }
  else  a2 = aa; 
  if (fl) { 
    typ = BT_ForeachFloat;
    blkok = true;
    f1 = atof(a1.c_str());
    f2 = atof(a2.c_str());
    if (hasa3)  df = atof(a3.c_str());
    else df = 1.;
    }
  else { 
    typ = BT_ForeachInt;
    blkok = true;
    i1 = atoi(a1.c_str());
    i2 = atoi(a2.c_str());
    if (hasa3)  di = atoi(a3.c_str());
    else di = 1;
    }
  }
}

/* --Methode-- */
PIACmdBloc::~PIACmdBloc()
{
for(int k=0; k<blocs.size(); k++) delete blocs[k];
}

/* --Methode-- */
PIACmdBloc* PIACmdBloc::Execute()
{
// cout << " DBG * PIACmdBloc::Execute() " << typ << " - " << bloclineid.size() <<
//      " I1,I2=" << i1 << " , " << i2 << " , " << di << endl;
string cmd;
int k=0;
int kj=0;
int kk=0;
char buff[32];
if (typ == BT_ForeachList)   // foreach string loop
  for(k=0; k<strlist.size(); k++) {
    cmd = "set " + varname + " '" + strlist[k] + "'";
    piacmd->Interpret(cmd);
    for(kj=0; kj<bloclineid.size(); kj++) {
      kk = bloclineid[kj]; 
      if (kk > 0)  piacmd->Interpret(lines[kk-1]);
      else blocs[-kk-1]->Execute();
      }
  }
else if (typ == BT_ForeachInt)  // Integer loop
  for(int i=i1; i<i2; i+=di) {
    k++;
    if (++k > 9999) {
      cout << ">>> Maximum PIACmdBloc loop limit (9999) -> break " << endl;
      break;
      }
    sprintf(buff, " %d", i);
    cmd = "set " + varname + buff;
    piacmd->Interpret(cmd);
    for(kj=0; kj<bloclineid.size(); kj++) {
      kk = bloclineid[kj]; 
      if (kk > 0)  piacmd->Interpret(lines[kk-1]);
      else blocs[-kk-1]->Execute();
      }
  }
else if (typ == BT_ForeachFloat)  // float loop
  for(float f=f1; f<f2; f+=df) {
    k++;
    if (++k > 9999) {
      cout << ">>> Maximum PIACmdBloc loop limit (9999) -> break " << endl;
      break;
      }
    sprintf(buff, " %g", f);
    cmd = "set " + varname + buff;
    piacmd->Interpret(cmd);
    for(kj=0; kj<bloclineid.size(); kj++) {
      kk = bloclineid[kj]; 
      if (kk > 0)  piacmd->Interpret(lines[kk-1]);
      else blocs[-kk-1]->Execute();
      }
  }

return(parent); 
}

// ------------------------------------------------------------
//         Classe PIACmd
// ------------------------------------------------------------

static PIACmd* curpiacmd = NULL;
/* --Methode-- */
PIACmd::PIACmd(NamedObjMgr* omg, PIStdImgApp* app)
{
mObjMgr = omg; 
mImgApp = app;
system("cp history.pic hisold.pic");
hist.open("history.pic");
histon = true;
trace = false;   timing = false;
gltimer = NULL;
curblk = NULL;
felevel = 0;
mulinecmd = "";
mulinefg = false;

cmdhgrp["All"] = 0;
cmdgrpid = 1;
cmdhgrp["Commands"] = 1;
helpwin = new PIAHelpWind(app, this);
helpwin->AddHelpGroup("All", 0);
helpwin->AddHelpGroup("Commands", 1);

string kw = "piacmd";
string usage;
usage = ">>> (piacmd) Interpreter's keywords : \n";
usage += "  > set varname string   # To set a variable, $varname \n";
usage += "  > get newvarname varname # To set a newvariable, equal to $varname \n";
usage += "  > setol varname patt     # Fills varname with object list \n";
usage += "  > unset varname          # clear variable definition \n";
usage += "  > echo string            # output string \n";
usage += "  > alias name string      # define a command alias \n";
usage += "  > readstdin varname      # reads a line from stdin into $varname \n";
usage += "  > foreach varname string-list # Loop \n"; 
usage += "  > for varname i1:i2[:di]      # Integer loop  \n"; 
usage += "  > for varname f1:f2[:df]      # Float loop  \n";
usage += "  > end                         # end loops \n"; 
usage += "  > if test then  # Conditional test : a == != < > <= >= b \n";
usage += "  > else          # Conditional  \n";
usage += "  > endif         # End of conditional if bloc \n"; 
usage += "  > break         # Delete (clears) all test and loop blocs \n";
usage += "  > return        # Stops command execution from a file \n";
usage += "  > listvars      # List of variable names and values \n";
usage += "  > listalias     # List of alias names and values \n";
usage += "  > listcommands  # List of all known commands \n";
usage += "  > exec filename # Execute commands from file \n";
usage += "  > shell comand_string  # Execute shell command \n";
usage += "  > help <command_name>  # <command_name> usage info \n"; 
usage += "  > helpwindow           # Displays help window \n";
usage += "  > timingon  timingoff traceon  traceoff \n";
string grp = "Commands";
RegisterHelp(kw, usage, grp);

basexec = new PIABaseExecutor(this, omg, app);
fitexec = new PIAFitter(this, app);
pawexec = new PAWExecutor(this, app);
CxxExecutor * cxxe = new CxxExecutor(this, app);
cxxexec = cxxe;
cxxoptwin = new  CxxOptionWind(app, cxxe);
cxxexwin  = new  CxxExecWind(app, cxxe);

AddInterpreter(this);
curcmdi = this;
}

/* --Methode-- */
PIACmd::~PIACmd()
{
hist.close();
if (gltimer) { delete gltimer;  gltimer = NULL; }
Modmap::iterator it;
for(it = modmap.begin(); it != modmap.end(); it++) {
  string name = (*it).first + "_end";
  DlModuleInitEndFunction fend = (*it).second->GetFunction(name);
  if (fend) fend();
  delete (*it).second;
  }
delete helpwin;
delete cxxexwin;
delete cxxoptwin;
if (curpiacmd == this)  curpiacmd = NULL;
delete basexec;
delete fitexec;
delete pawexec;
delete cxxexec;
}

/* --Methode-- */
PIACmd* PIACmd::GetInterpreter()
{
return(curpiacmd);
}

/* --Methode-- */
string PIACmd::Name()
{
return("piacmd");
}

/* --Methode-- */
void PIACmd::RegisterCommand(string& keyw, string& usage, CmdExecutor * ce, string grp)
{
if (!ce) { 
  RegisterHelp(keyw, usage, grp);
  return;
  }
int gid = CheckHelpGrp(grp);
cmdex cme;
cme.group = gid;
cme.us = usage;
cme.cex = ce;
cmdexmap[keyw] = cme;
}

/* --Methode-- */
void PIACmd::RegisterHelp(string& keyw, string& usage, string& grp)
{
int gid = CheckHelpGrp(grp);
cmdex cme;
cme.group = gid;
cme.us = usage;
cme.cex = NULL;
helpexmap[keyw] = cme;
}

/* --Methode-- */
int PIACmd::CheckHelpGrp(string& grp)
{
int gid=0;
CmdHGroup::iterator it = cmdhgrp.find(grp);
if (it == cmdhgrp.end()) {
  cmdgrpid++;   gid = cmdgrpid;
  cmdhgrp[grp] = gid;
  helpwin->AddHelpGroup(grp.c_str(), gid);
  }
else gid = (*it).second;
return(gid);
}

/* --Methode-- */
void PIACmd::UpdateHelpList(PIAHelpWind* hw, int gid)
{
helpwin->ClearHelpList();
CmdExmap::iterator it;
for(it = helpexmap.begin(); it != helpexmap.end(); it++) {
  if ( (gid != 0) && ((*it).second.group != gid) ) continue;
  helpwin->AddHelpItem((*it).first.c_str());
  }
for(it = cmdexmap.begin(); it != cmdexmap.end(); it++) {
  if ( (gid != 0) && ((*it).second.group != gid) ) continue;
  helpwin->AddHelpItem((*it).first.c_str());
  }
}

/* --Methode-- */
void PIACmd::LoadModule(string& fnameso, string& name)
{
PDynLinkMgr * dynlink = new PDynLinkMgr(fnameso, false);
if (dynlink == NULL) {
  cerr << "PIACmd/LoadModule_Error: Pb opening SO " << fnameso << endl;
  return;
  }
string fname = name + "_init";
DlModuleInitEndFunction finit = dynlink->GetFunction(fname);
if (!finit) {
  cerr << "PIACmd/LoadModule_Error: Pb linking " << fname << endl;
  return;
  }
cout << "PIACmd/LoadModule_Info: Initialisation module" << name 
     << "  " << fname << "() ..." << endl;
finit();
modmap[name] = dynlink;
return;
}

/* --Methode-- */
void PIACmd::AddInterpreter(CmdInterpreter * cl)
{
if (!cl) return;
interpmap[cl->Name()] = cl;}

/* --Methode-- */
void PIACmd::SelInterpreter(string& name)
{
InterpMap::iterator it = interpmap.find(name);
if (it == interpmap.end())   return;
curcmdi = (*it).second;
}



/* Fonction */
static string GetStringFrStdin(PIACmd* piac)
{
PIStdImgApp* piapp = piac->GetImgApp();
if (piapp) {
  PIBaseWdg* wdg = piapp->CurrentBaseWdg();
  if (wdg)  wdg->Refresh();
#ifndef __mac__
  /* On vide le buffer X-Window */
  XSync(PIXDisplay(),False);
#endif
}
char buff[128];
fgets(buff, 128, stdin);
buff[127] = '\0';
return((string)buff);
}

/* --Methode-- */
int PIACmd::Interpret(string& s)
{
int rc = 0;
NamedObjMgr omg;

// On saute de commandes vides
size_t l;
l = s.length();
if (l < 1)  return(0);

// On enregistre les commandes 
if (histon) hist << s << endl;   

if (s[0] == '#') return(0); // si c'est un commentaire

// Logique de gestion des lignes suite
// un \ en derniere position indique la presence d'une ligne suite
size_t lnb = s.find_last_not_of(' ');
if (s[lnb] == '\\' ) { // Lignes suite ...
  mulinefg = true;
  mulinecmd += s.substr(0,lnb);
  mImgApp->GetConsole()->SetPrompt("...? ");
  return(0);
}

if (mulinefg) {  // Il y avait des lignes suite
  s = mulinecmd + s;
  mulinecmd = "";
  mulinefg = false;
  const char * rprompt = (curblk == NULL) ? "Cmd> " : "for...? ";
  mImgApp->GetConsole()->SetPrompt(rprompt);
}

// Removing leading blanks
size_t p,q;

p=s.find_first_not_of(" \t");
if (p < l) s = s.substr(p);

// >>>> Substitution d'alias (1er mot)
CmdVarList::iterator it;
p = 0;
q = s.find_first_of(" \t");
l = s.length();
string w1 =  (q < l) ? s.substr(p,q-p) : s.substr(p);
it = mAliases.find(w1);
if (it != mAliases.end())  { 
  s =  (q < l) ? ((*it).second + s.substr(q)) : (*it).second ;
  l = s.length();
  p=s.find_first_not_of(" \t");
  if (p < l) s = s.substr(p);
  p = 0;
  q = s.find_first_of(" ");
  }

// >>>> Separating keyword 
string toks,kw;
if (q < l)
  {  kw = s.substr(p,q-p);  toks = s.substr(q, l-q); }
else { kw = s.substr(p,l-p);  toks = ""; }

// les mot-cle end else endif doivent etre le seul mot de la ligne
if ( (kw == "end") || (kw == "else") || (kw == "endif") ) {
  size_t ltk = toks.length();
  if (toks.find_first_not_of(" \t") < ltk) {
    cerr << "PIACmd::Interpret()/syntax error near end else endif \n"
	 << "line: " << s << endl;
    return(1);
  }
}

if (kw == "break") {
  PIACmdBloc* curb = curblk;
  while (curb) {
    PIACmdBloc* dblk = curb;
    curb = curb->Parent();
    delete dblk;
  }
  testresult.clear();
  return(0);
}
else if (kw == "return")  return(77777);

// On verifie si nous sommes dans un bloc (for , foreach)
if (curblk != NULL)  { // On est dans un bloc
  if ( (kw == "for") || (kw == "foreach")) felevel++;
  else if (kw == "end") felevel--;
  if (felevel == 0) { // Il faut executer le bloc
    PIACmdBloc* curb = curblk;
    curblk = curb->Parent();
    mImgApp->GetConsole()->SetPrompt("Cmd> ");
       //       cout << " *DBG* Executing bloc " << endl;
    bool ohv = histon;
    histon = false;
    curb->Execute();
    histon = ohv;
  }
  else curblk->AddLine(s); 
  return(0);
}
else if (kw == "end") {
  cerr << "PIACmd::Interpret()/syntax error - end outside for/foreach bloc \n"
       << "line: " << s << endl;
  return(1);  
}

// Sommes-nous dans un bloc de test if then else 
if (testresult.size() > 0) {  // Nous sommes ds un bloc if 
  if (kw == "else") {
    if ((*tresit) & 2) {
      cerr << "PIACmd::Interpret()/syntax error - multiple else in if bloc \n"
	   << "line: " << s << endl;
      return(1);
    }
    else { 
      const char * npr = ((*tresit)&1) ? "else-F> " : "else-T> ";
      mImgApp->GetConsole()->SetPrompt(npr);
      (*tresit) |= 2;
      return(0);
    }
  }
  else if (kw == "endif") {
    list<char>::iterator dbit = tresit;
    tresit--;
    testresult.erase(dbit);
    const char * npr = "Cmd> ";
    if (testresult.size() > 1) {
      if (!((*tresit)&2)) 
	npr = ((*tresit)&1) ? "if-T> " : "if-F> ";
      else 
	npr = ((*tresit)&1) ? "else-F> " : "else-T> ";
    }
    mImgApp->GetConsole()->SetPrompt(npr);
    return(0);
  }
} 
else if ((kw == "else") || (kw == "endif")) {
  cerr << "PIACmd::Interpret()/syntax error - else,endif outside if bloc \n"
       << "line: " << s << endl;
  return(1);  
}

bool fgcont = true;
if (testresult.size() > 0) { // Resultat de if ou else
  list<char>::iterator it;
  for(it=testresult.begin(); it!=testresult.end(); it++) {
    // Si on n'est pas ds le else et le if est faux
    if ( !((*it)&2) && !((*it)&1) )  fgcont = false;
    // Si on est ds else et le if etait vrai !
    if ( ((*it)&2) && ((*it)&1) )  fgcont = false;
    if (!fgcont)  break;
  }
} 

if ((!fgcont) && (kw != "if"))  return(0);


// Nous ne sommes donc pas dans un bloc ....  Substitution de variables
string s2;
int rcs ;
// Execution de code C++

if (s[0] == '@')   { 
  CxxExecutor * cxxe = dynamic_cast<CxxExecutor *>(cxxexec);
  if (cxxe == NULL) {
    cerr << "PIACmd::Interpret() - BUG !!! Not a CxxExecutor " << endl;
    return(99);
  }
  // Sans substitution des variables $
  if (s[1] == '@') return(cxxe->ExecuteCXX(s.substr(2)));
  else { // AVEC substitution des variables $
    rcs = SubstituteVars(s, s2);
    if (rcs) return(rcs);
    return(cxxe->ExecuteCXX(s2.substr(1)));    
  }
}


rcs = SubstituteVars(s, s2);
if (rcs) return(rcs);

// >>>> Separating keyword and tokens 
vector<string> tokens;

q = s2.find(' '); 
l = s2.length();
if (q < l)
  {  kw = s2.substr(0,q);  toks = s2.substr(q, l-q); }
else { kw = s2;  toks = ""; }

q = 0;
while (q < l)  {
  p = toks.find_first_not_of(" \t",q+1); // au debut d'un token
  if (p>=l) break;
  if ( (toks[p] == '\'') || (toks[p] == '"') ) {
    q = toks.find(toks[p],p+1);
    if (q>=l)  { 
      cerr << "PIACmd::Interpret()/Syntax Error - Unbalenced quotes " << toks[p] << '.' << endl;
      return(2);
    }
    p++;
  }
  else {
    q = toks.find_first_of(" \t",p); // la fin du token;
  }
  string token = toks.substr(p,q-p);
  tokens.push_back(token);
  }


// Si c'est un for/foreach, on cree un nouveau bloc
if ((kw == "foreach") || (kw == "for")) {
  //     cout << " *DBG* We got a foreach... " << endl;
  PIACmdBloc* bloc = new PIACmdBloc(this, curblk, kw, tokens);
  if (!bloc->CheckOK()) {
    cerr << "PIACmd::Interpret() for/foreach syntax Error ! " << endl;
    delete bloc;
    return(1);
    }
  felevel++;
  if (curblk)  curblk->AddBloc(bloc);
  else  mImgApp->GetConsole()->SetPrompt("for...> ");
  curblk = bloc;
  //  cout << " *DBG* New Bloc created ... " << endl;
  return(0);
  }
else if (kw == "if") {  // Un test if
  bool restst = true;
  int rct = EvaluateTest(tokens, s, restst);
  if (rct) {
    cerr << "PIACmd::Interpret() if syntax Error ! " << endl;
    return(1);
  }
  char res_tst = (restst) ? 1 : 0;
  testresult.push_back(res_tst);
  if (testresult.size() == 1) tresit = testresult.begin();
  else tresit++;
  const char * npr = (restst) ? "if-T> " : "if-F> ";
  mImgApp->GetConsole()->SetPrompt(npr);
}
//  Execution de commandes 
else rc = ExecuteCommandLine(kw, tokens, toks);
return(rc);

// cout << "PIACmd::Do() DBG  KeyW= " << kw << " NbArgs= " << tokens.size() << endl;
//  for(int ii=0; ii<tokens.size(); ii++)
//  cout << "arg[ " << ii << " ] : " << tokens[ii] << endl;

}

/* --Methode-- */
int PIACmd::SubstituteVars(string & s, string & s2)
//  Variable substitution  
{
NamedObjMgr omg;

size_t p,q,q2,l;

s2="";
p = 0;
l = s.length();
string vn;
while (p < l) {
  q = s.find('$',p);
  if (q > l) break;
  q2 = s.find('\'',p);
  if ((q2 < l) && (q2 < q)) {  // On saute la chaine delimitee par ' '
    q2 = s.find('\'',q2+1);
    if (q2 >= l) {
      cerr << " Syntax error - Unbalenced  quotes !!! " << endl;
      return(1);
      }
    s2 += s.substr(p, q2-p+1);
    p = q2+1;  continue;  
  }
  //  cout << "DBG: " << s2 << " p= " << p << " q= " << q << " L= " << l << endl;
  if ((q>0) && (s[q-1] == '\\')) {   // Escape character \$
     s2 += (s.substr(p,q-1-p) + '$') ; p = q+1;
     continue;
     }
  if (q >= l-1) {
      cerr << " Syntax error - line ending with $ !!! " << endl;
      return(2);
      }
  vn = "";
  if ( s[q+1] == '{' ) {  // Variable in the form ${name}
    q2 = s.find('}',q+1);
    if (q2 >= l) {
      cerr << " Syntax error -  Unbalenced  brace {} !!! " << endl;
      return(3);
      }
    vn = s.substr(q+2,q2-q-2);
    q2++;
    }
  else if ( s[q+1] == '[' ) {  // Variable in the form $[varname]  -> This is $$varname
    q2 = s.find(']',q+1);
    if (q2 >= l) {
      cerr << " Syntax error - Unbalenced  brace [] !!! " << endl;
      return(4);
      }
    vn = s.substr(q+2,q2-q-2);
    if ( (vn.length() < 1) || (!omg.HasVar(vn)) )  {
      cerr << " Error: Undefined variable " << vn << " ! " << endl;
      return(5);
      }
    vn = omg.GetVar(vn);
    q2++;
    }
  else { 
    q2 = s.find_first_of(" .:+-*/,[](){}&|!$\"'",q+1); 
    if (q2 > l) q2 = l;
    vn = s.substr(q+1, q2-q-1); 
    }
  if ( (vn.length() < 1) || (!omg.HasVar(vn)) )  {
    cerr << " Error: Undefined variable " << vn << " ! " << endl;
    return(5);
    }
  s2 += (s.substr(p, q-p) + omg.GetVar(vn));
  p = q2;
  } 
if (p < l) s2 += s.substr(p);

p = s2.find_first_not_of(" \t");
if (p < l) s2 = s2.substr(p);

return(0);
}

/* --Methode-- */
int PIACmd::EvaluateTest(vector<string> & args, string & line, bool & res)
{
  res = true;
  if ((args.size() < 4) || (args[3] != "then")) return(1);
  if (args[1] == "==") res = (args[0] == args[2]);
  else if (args[1] == "!=") res = (args[0] != args[2]);
  else if (args[1] == "<") 
    res = (atof(args[0].c_str()) < atof(args[2].c_str()));
  else if (args[1] == ">") 
    res = (atof(args[0].c_str()) > atof(args[2].c_str()));
  else if (args[1] == "<=") 
    res = (atof(args[0].c_str()) <= atof(args[2].c_str()));
  else if (args[1] == ">=") 
    res = (atof(args[0].c_str()) >= atof(args[2].c_str()));
  else return(2);
  return(0);
}

/* --Methode-- */
int PIACmd::ExecuteCommandLine(string & kw, vector<string> & tokens, string & toks)
{
int rc = 0;
NamedObjMgr omg;

// >>>>>>>>>>> Commande d'interpreteur
if (kw == "helpwindow") ShowHelpWindow();
else if (kw == "help") {
  if (tokens.size() > 0) cout << GetUsage(tokens[0]) << endl;
  else {  
    string kwh = "piacmd";
    cout << GetUsage(kwh) << endl;
    }
  }

else if (kw == "set") {
  if (tokens.size() < 2) { cout << "PIACmd::Interpret() Usage: set varname string" << endl;  return(0); }
  if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
    cerr << "PIACmd::Interpret()/Error Variable name should start with alphabetic" << endl;
    return(0);
    }
  //  string xx = tokens[1];
  //  for (int kk=2; kk<tokens.size(); kk++)  xx += (' ' + tokens[kk] );
  omg.SetVar(tokens[0], tokens[1]);
  }

else if (kw == "getvar") {
  if (tokens.size() < 2) { cout << "PIACmd::Interpret() Usage: getvar newvarname varname" << endl;  return(0); }
  if (!omg.HasVar(tokens[1])) {
    cerr << "Error - No " << tokens[1] << " Variable " << endl;
    return(0);
    } 
  if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
    cerr << "PIACmd::Interpret()/Error Variable name should start with alphabetic" << endl;
    return(0);
    }
  omg.SetVar(tokens[0], omg.GetVar(tokens[1]) );
  }

else if (kw == "alias") {
  if (tokens.size() < 2) { cout << "PIACmd::Interpret() Usage: alias aliasname string" << endl;  return(0); }
  if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
    cerr << "PIACmd::Interpret()/Error alias name should start with alphabetic" << endl;
    return(0);
    }
  string xx = tokens[1];
  for (int kk=2; kk<tokens.size(); kk++)  xx += (' ' + tokens[kk]);
  mAliases[tokens[0]] = xx;
  }

else if (kw == "setol") {
  if (tokens.size() < 2) { cout << "PIACmd::Interpret() Usage: setol varname objnamepattern" << endl;  return(0); }
  if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
    cerr << "PIACmd::Interpret()/Error Variable name should start with alphabetic" << endl;
    return(0);
    }
  vector<string> ol;
  mObjMgr->GetObjList(tokens[1], ol);
  string vol; 
  if (ol.size() < 1) vol = "";
  else { 
     vol = ol[0]; 
    for (int kk=1; kk<ol.size(); kk++)  vol += (' ' + ol[kk]);
    }
  omg.SetVar(tokens[0], vol);
  }
else if (kw == "unset") {
  if (tokens.size() < 1) { cout << "PIACmd::Interpret() Usage: unset varname" << endl;  return(0); }
  if (omg.HasVar(tokens[0])) omg.DeleteVar(tokens[0]) ;
  else cerr << "PIACmd::Interpret() No variable with name " << tokens[0] << endl;
  }
else if (kw == "echo") {
  for (int ii=0; ii<tokens.size(); ii++)
    cout << tokens[ii] << " " ;
  cout << endl;
 }
else if (kw == "readstdin") {
  if (tokens.size() < 1) { cout << "PIACmd::Interpret() Usage: readstdin varname" << endl;  return(0); }
  if ((tokens[0].length() < 1) || !isalpha((int)tokens[0][0]) ) {
    cerr << "PIACmd::Interpret()/Error Variable name should start with alphabetic" << endl;
    return(0);
    }
  mImgApp->GetConsole()->AddStr(">>> Reading From StdIn \n", PIVA_Magenta);
  cout << tokens[0] << " ? " << endl;
  omg.SetVar(tokens[0], GetStringFrStdin(this) );
  }

else if (kw == "listvars") {
  cout << "PIACmd::Interpret()  Variable List , VarName = Value \n";
  DVList& varlist = omg.GetVarList();
  DVList::ValList::const_iterator it;
  string value;
  for(it = varlist.Begin(); it != varlist.End(); it++) {
#ifdef SANS_EVOLPLANCK
    MuTyV mtv = (*it).second;
    value = (string)(mtv);
#else
    value = (string)((*it).second.elval);
#endif
    cout << (*it).first << " = " <<  value << "\n";
    }
  cout << endl;
  }
else if (kw == "listalias") {
  cout << "PIACmd::Interpret()  Alias List , AliasName = Value \n";
  CmdVarList::iterator it;
  for(it = mAliases.begin(); it != mAliases.end(); it++)  
    cout << (*it).first << " = " <<  (*it).second << "\n";
  cout << endl;
  }
else if (kw == "listcommands") {
  cout << "---- PIACmd::Interpret()  Command Variable List ----- \n";
  CmdExmap::iterator it;
  int kc = 0;
  for(it = cmdexmap.begin(); it != cmdexmap.end(); it++) {
    cout << (*it).first << "  ";
    kc++;
    if (kc >= 5) { cout << "\n"; kc = 0; }
    }
  cout << endl;
  }
else if (kw == "traceon")  { cout << "PIACmd::Interpret()  -> Trace ON mode " << endl; trace = true; }
else if (kw == "traceoff") { cout << "PIACmd::Interpret()  -> Trace OFF mode " << endl; trace = false; }
else if (kw == "timingon") { 
  cout << "PIACmd::Interpret()  -> Timing ON mode " << endl; 
  if (gltimer)   delete gltimer;   gltimer = new Timer("PIA-CmdInterpreter ");   timing = true; 
  }
else if (kw == "timingoff") { 
  cout << "PIACmd::Interpret()  -> Timing OFF mode " << endl; 
  if (gltimer)  delete gltimer;  gltimer = NULL;  timing = false; 
  }
else if (kw == "exec") {
  if (tokens.size() < 1) { cout << "PIACmd::Interpret() Usage: exec filename" << endl;  return(0); }
  ExecFile(tokens[0], tokens);
  }
else if (kw == "shell") {
  if (tokens.size() < 1) { cout << "PIACmd::Interpret() Usage: shell cmdline" << endl;  return(0); }
  string cmd;
  for (int ii=0; ii<tokens.size(); ii++)
    cmd += (tokens[ii] + ' ');
  system(cmd.c_str());
  }

//  Execution d'une commande enregistree
else rc = ExecuteCommand(kw, tokens, toks);

if (timing)  gltimer->Split();
return(rc);
}

/* --Methode-- */
int PIACmd::ParseLineExecute(string& line)
{
vector<string> tokens;

if (line.length() < 1)  return(0);

string toks,kw;
size_t p = line.find_first_not_of(" ");
line = line.substr(p);
p = 0;
size_t q = line.find_first_of(" ");
size_t l = line.length();

if (q < l)
  {  kw = line.substr(p,q-p);  toks = line.substr(q, l-q); }
else { kw = line.substr(p,l-p);  toks = ""; }

q = 0;
while (q < l)  {
  p = toks.find_first_not_of(" ",q+1); // au debut d'un token
  if (p>=l) break;
  q = toks.find_first_of(" ",p); // la fin du token;
  string token = toks.substr(p,q-p);
  tokens.push_back(token);
  }

return(ExecuteCommand(kw, tokens, toks));  
}

/* --Methode-- */
int PIACmd::ExecuteCommand(string& keyw, vector<string>& args, string& toks)
{
  int rc = -1;
  CmdExmap::iterator it = cmdexmap.find(keyw);
  if (it == cmdexmap.end())  cout << "No such command : " << keyw << " ! " << endl;
  else { 
    if ((*it).second.cex) rc = (*it).second.cex->Execute(keyw, args, toks);
    else cout << "Dont know how to execute " << keyw << " ? " << endl;
    }
  return(rc);
}

/* --Methode-- */
int PIACmd::ExecFile(string& file, vector<string>& args)
{
char line_buff[512];
FILE *fip;

if ( (fip = fopen(file.c_str(),"r")) == NULL ) {
  if (file.find('.') >= file.length()) {
    cout << "PIACmd::Exec(): Error opening file " << file << endl;
    file += ".pic";
    cout << "                Trying file " << file << endl;
    fip = fopen(file.c_str(),"r");
    }
  }

if(fip == NULL) {
  cerr << "PIACmd::Exec() Error opening file " << file << endl;
  hist << "##! PIACmd::Exec() Error opening file " << file << endl;
  return(0);
  }

// hist << "### Executing commands from " << file << endl;

// Setting $0 ... $99 variables 
int k;
char buff[32];
// First, we clear all previous values
NamedObjMgr omg;
string vn="#";
omg.DeleteVar(vn); 
for(k=0; k<99; k++) {   
  sprintf(buff,"%d",k);
  vn = buff;
  omg.DeleteVar(vn); 
  }
// We then set them
string vval;
vn="#";
sprintf(buff,"%d",(int)args.size());
omg.SetVar(vn, buff);
for(k=0; k<args.size(); k++) {   
  sprintf(buff,"%d",k);
  vn = buff;
  omg.SetVar(vn, args[k]);
  }

if (trace) { 
  mImgApp->GetConsole()->AddStr("### Executing commands from ", PIVA_Magenta);
  mImgApp->GetConsole()->AddStr(file.c_str(), PIVA_Magenta);
  mImgApp->GetConsole()->AddStr("\n", PIVA_Magenta);
  }

bool ohv = histon;
histon = false; 
while (fgets(line_buff,511,fip) != NULL)
  {
  if (trace) mImgApp->GetConsole()->AddStr(line_buff, PIVA_Magenta);
  line_buff[strlen(line_buff)-1] = '\0';   /*  LF/CR de la fin */
  string line(line_buff);
  if (Interpret(line) == 77777) break;
  }
histon = ohv; 

// hist << "### End of Exec( " << file << " ) " << endl;
if (trace) { 
  mImgApp->GetConsole()->AddStr("### End of Exec( ", PIVA_Magenta);
  mImgApp->GetConsole()->AddStr(file.c_str(), PIVA_Magenta);
  mImgApp->GetConsole()->AddStr(" ) \n", PIVA_Magenta);
  }

return(0);
}


static string* videstr = NULL;
/* --Methode-- */
string& PIACmd::GetUsage(const string& kw)
{
bool fndok = false;
CmdExmap::iterator it = cmdexmap.find(kw);
if (it == cmdexmap.end()) {
  it = helpexmap.find(kw);
  if (it != helpexmap.end())  fndok = true;
  }
  else  fndok = true; 
if (fndok)   return( (*it).second.us ); 
// Keyword pas trouve
if (videstr == NULL) videstr = new string("");
*videstr =  "Nothing known about " + kw + " ?? ";
return(*videstr);
 
}

/* --Methode-- */
void PIACmd::ShowHelpWindow()
{
helpwin->Show();
}

/* --Methode-- */
void PIACmd::ShowCxxOptionWindow()
{
cxxoptwin->Show();
}

/* --Methode-- */
void PIACmd::ShowCxxExecWindow()
{
cxxexwin->Show();
}

/* --Methode-- */
void PIACmd::HelptoLaTex(string const & fname)
{
FILE *fip;
if ((fip = fopen(fname.c_str(), "w")) == NULL)   { 
  cout << "PIACmd::HelptoLaTex_Error: fopen( " << fname << endl; 
  return;
  }

fputs("% ----- Liste des groupes de Help ----- \n",fip);
fputs("List of {\\bf piapp} on-line Help groups: \n", fip);
fputs("\\begin{itemize} \n",fip);
CmdHGroup::iterator it;
for(it = cmdhgrp.begin(); it != cmdhgrp.end(); it++) 
  fprintf(fip,"\\item {\\bf %s }  (p. \\pageref{%s}) \n", 
         (*it).first.c_str(), (*it).first.c_str());

fputs("\\end{itemize} \n",fip);

fputs("\\newpage \n",fip);

CmdExmap::iterator ite;
fputs("% ----- Liste de toutes les commandes ----- \n",fip);
fputs("\\begin{center} \n ", fip);
fputs("\\rule{2cm}{1mm} List of {\\bf piapp} Help items \\rule{2cm}{1mm} \n", fip);
fputs("\n \n  \\vspace{5mm} \n",fip);
fputs("\\begin{tabular}{llllll}  \n", fip);
int kt = 0;
for(ite = helpexmap.begin(); ite != helpexmap.end(); ite++) {
  fprintf(fip,"%s & p. \\pageref{%s} ", (*ite).first.c_str(),  (*ite).first.c_str() );
  kt++;
  if (kt < 3) fputs(" & ", fip);
  else  { fputs(" \\\\  \n", fip);  kt = 0; }
  }
if (kt == 1) fputs("  &  &  &   \\\\  \n", fip);
else if (kt == 2)  fputs("  &   \\\\  \n", fip);
fputs("\\end{tabular} \n", fip);
fputs("\\end{center} \n", fip);
fputs("\n \n \\vspace{1cm} \n",fip);

fputs("\\begin{center} \n ", fip);
fputs("\\rule{2cm}{1mm} List of {\\bf piapp} Commands \\rule{2cm}{1mm} \n", fip);
fputs("\n \n  \\vspace{5mm} \n",fip);
fputs("\\begin{tabular}{llllll} \n", fip);
kt = 0;
for(ite = cmdexmap.begin(); ite != cmdexmap.end(); ite++) {
  fprintf(fip,"%s & p. \\pageref{%s} ", (*ite).first.c_str(), (*ite).first.c_str() );
  kt++;
  if (kt < 3) fputs(" & ", fip);
  else  { fputs(" \\\\  \n", fip);  kt = 0; }
  }
if (kt == 1) fputs("  &  &  &   \\\\  \n", fip);
else if (kt == 2)  fputs("  &   \\\\  \n", fip);
fputs("\\end{tabular} \n", fip);
fputs("\\end{center} \n", fip);
// fputs("\\newline \n",fip);

fputs("% ----- Liste des commandes dans chaque groupe ----- \n",fip);
fputs("\\newpage \n",fip);
int gid;
for(it = cmdhgrp.begin(); it != cmdhgrp.end(); it++) {
  gid = (*it).second;
  if (gid == 0)  continue;
  fprintf(fip,"\\subsection{%s} \\label{%s} \n", 
          (*it).first.c_str(), (*it).first.c_str());
  for(ite = helpexmap.begin(); ite != helpexmap.end(); ite++) {
    if ((*ite).second.group != gid)  continue;
    fprintf(fip,"{ \\Large $ \\star \\star \\star $ }  Help item {\\bf \\Large %s } \\label{%s} \n", 
            (*ite).first.c_str(), (*ite).first.c_str());
    fputs("\\begin{verbatim} \n",fip);
    fprintf(fip,"%s\n", (*ite).second.us.c_str());
    fputs("\\end{verbatim} \n",fip);
    }
  for(ite = cmdexmap.begin(); ite != cmdexmap.end(); ite++) {
    if ((*ite).second.group != gid)  continue;
    fprintf(fip,"{ \\Large $ \\star \\star \\star $ }  Command {\\bf \\Large %s } \\label{%s} \n", 
            (*ite).first.c_str(), (*ite).first.c_str());
    fputs("\\begin{verbatim} \n",fip);
    fprintf(fip,"%s\n", (*ite).second.us.c_str());
    fputs("\\end{verbatim} \n",fip);
    }
}

fclose(fip);
return;
}



