#include "Action.h"
#include "DBError.h"
#include "DBHandler.h"

extern EnvObject * userdata;
extern DBGate * dbgate;
extern HTTP * http;
extern Security * security;
extern Configuration * config;
extern string LoopKey;

Action::Action () {

  string fname;

  //  cout << "\n\n **** trying to make a new Action" ;

  /* open up the last page */
  fname = config->getBasePageDir() + (security->getLastPage());
  parser->newFile (fname);

  //update the LoopInfo object 
  loopinfo->setFile( security->getLastPage() );

  //  cout << "\n **** the action is using file: " << fname;

  // NOTE: the "this" namespace was set up in the Check section
}


Error Action::doAction (void) {
  Error err;
  SGMLTokenList & stl = parser->actionTokens();

  if (parser->errors().numFatalErrors() > 0 ||
      parser->errors().numWarnings() > 0){
    outputErrors(parser->errors(),security->getLastPage());
  }

  // tell the userdata object wo we are
  userdata->setExec (this);

  if (stl.size() != 0) {
    try {
      recurseOnTokens ( stl , err );
    }
    catch (ExecutorAbort ea){
      // Return gracefully
    }
  }

  return (err);

}


/* look at each token, and make it 'go' */
void Action::recurseOnTokens (SGMLTokenList & tokenlist , Error & err) {
  SGMLTokenList::iterator i;

  i = tokenlist.begin();

  while (i != tokenlist.end()) {
    callFunction ( *i, err);
    i++;
  }
}




void Action::callFunction (SGMLToken * t, Error & err) {
  string command = t->command();
  char test[255];
  strcpy (test, command.c_str());

  if (command == "ERROR") {
    processERROR ( (SGMLContainer *) t, err);
  } else if (command == "LOOP") {
    processLOOP ( (SGMLContainer *) t, err);
  } else if (command == "DATFORM" || command == "SBFORM") {
    processDATFORM ( (SGMLContainer *) t, err);
  } else if (command == "REDIRECT") {
    processREDIRECT ( (SGMLTag *) t, err);
  } else {
    BaseSBModule::callFunction (t, err);
  }
}

/*
@nodoc
*/

void Action::processLOOP (SGMLContainer * t, Error & err) {
  AssocArray * flags = &(t->flags());
  AssocArrayPtrDLList * data = NULL; // the stuff we are looping over
  string OrigLoopKey = LoopKey;
  string name;
  int i;
  char temp [10]; //hack!
  int length;
  EnvObject * old_userdata = userdata;

  interpretString ((*flags)["NAME"], name);  //get the name of the loop
  name = OrigLoopKey + "_" + name;

  if (flags->contains("WHILE")){ // Support both BOOL and WHILE
    (*flags)["BOOL"] = (*flags)["WHILE"];
  }

  if (  ! hasDataSource ( (SGMLTag *) t) ) {
    // get the number of times this loop has played before
    // and init data to the right number of empty assoc arrays
    length = loopinfo->getLoopInfo (name);
    data = new AssocArrayPtrDLList();
    for (int i=0;i<length;i++) {
      AssocArray * a = new AssocArray();
      data->push_back(a);
    }
  } else if ( ! flags->contains("BOOL") ) {
    //they specified a non-dynamic data source
    try {
	data = getList (t);  // get the data they want to loop over
    }
    catch (DBError dbe) {
      if (!handle (dbe, err)) {
        throw ("Unhandled exception thrown by LOOP tag");
      } else {
        data = new AssocArrayPtrDLList();
      }
    }
  }

  if (data != NULL) {
    length = data->size();

    // open up a  new environemnt
    userdata = new EnvObject (userdata);

    for (i = 0; i < length; i++) {
      AssocArray * aaptr;
      sprintf (temp, "%d", i);
      LoopKey = name + "_" + temp;

      // grab the first 'row' of info that we are looping over
      aaptr = data->front();
      data->pop_front();

      // store this in the namespace = flags[NAME]
      userdata->setNameSpace ((*flags)["NAME"], aaptr);
      recurseOnTokens ( t->contents() , err);
    }

    // clean up and reset environment
    delete (userdata);
    userdata = old_userdata;

    delete (data);
  } else if ( flags->contains("BOOL") ) {

    // open up a  new environemnt
    userdata = new EnvObject (userdata);

    length = 0;
    while ( evalSimpleBool((SGMLTag *) t, err) ) {
      sprintf (temp, "%d", length++);
      LoopKey = name + "_" + temp;

      // store this in the namespace = flags[NAME]
      userdata->setNameSpace ((*flags)["NAME"], new AssocArray());
      recurseOnTokens ( t->contents() , err );
    }

    // clean up and reset environment
    delete (userdata);
    userdata = old_userdata;
  }

  // save the loopinfo so we can access it by name later
  // if this is a nested loop, only save it if they INSIST on
  // it since the performance penelty is huge
  if (OrigLoopKey == "" || flags->contains("SAVELOOPINFO") ) {
    loopinfo->setLoopInfo (name, length);
  }
  
  LoopKey = OrigLoopKey;

}

/*
@nodoc
    this function will check to see if a hidden var that was embeded
    in the DATFORM created by the Sender is in the http object's data
    if it is, it will recurse on the tokens contents */
void Action::processDATFORM (SGMLContainer * t, Error & err) {
  AssocArray * flags = &(t->flags());
  string temp;

  if ( (t->flags().contains("NAME")) ) {
    temp = "form_" + (*flags)["NAME"];
    if ((http->data).contains(temp)){
      recurseOnTokens (t->contents(), err);
    }
  } else {
    // they didn't give a name flag!
    err.addWarning ("SBFORM tag without NAME flag in ACTION section");
  }

}

/*
@usehtml
@seealso _SBREF_ _SBFORM_
@usage <REDIRECT SCREEN="filename">
@IFLAG SCREEN Destination SteelBlue filename (relative to BasePages)

@sections CHECK, ACTION
@index Changes the destination SteelBlue page.

Changes the destination SteelBlue page from one given in
an _SBFORM_ or _SBREF_ tag.

@ex <HTML>
@ex  <BODY>
@ex   <SBREF SCREEN="somewhere">
@ex    Go somewhere
@ex   </SBREF>
@ex  </BODY>
@ex </HTML>
@ex  
@ex <ACTION>
@ex  <!-- redirect destination to the welcome page -->
@ex  <redirect screen="welcome.sb">
@ex </ACTION>
*/

void Action::processREDIRECT (SGMLTag * t, Error & err) {
  AssocArray * flags = &(t->flags());
  string dest = interpretString ((*flags)["SCREEN"]);
  
  security->setDest (dest);
}

/*
@usehtml
@seealso _SHOWERRORS_
@usage <ERROR TEXT="string">
@IFLAG TEXT Text to appear in the error message

@sections CHECK, ACTION
@index Causes the previous HTML page to be redisplayed with the given error message.

Causes the previous HTML page to be redisplayed with the given
error message.  If an ERROR flag is in the CHECK section,
the rest of the CHECK section is executed (but not the ACTION
section) and then the HTML section is redisplayed.  If an ERROR
flag is in the ACTION section, the rest of the ACTION section
is executed and then the HTML section is redisplayed.  Input boxes
in the HTML section are automatically restored for the _SBFORM_ that
was submitted.  Additional ERROR flags will cause a list of
errors to be displayed.

@ex <HTML>
@ex  <BODY>
@ex   <showerrors>
@ex   <sbref screen="welcome.sb">
@ex     Press to make an error
@ex   </sbref>
@ex  </BODY>
@ex </HTML>
@ex <CHECK>
@ex  <ERROR text="This is an error">
@ex  <ERROR text="This is another error">
@ex </CHECK>
 */

void Action::processERROR (SGMLContainer * t, Error & err) {
  AssocArray * flags = &(t->flags());
  string error_text = interpretString ((*flags)["TEXT"]);
  string old_err_fname = err.getFilename();
  string emptyname = "";

  err.setFilename(emptyname); // surpress printing of filename in error
  err.addFatalError (error_text);
  err.setFilename(old_err_fname);

}

/*
@NODOC 
*/
void Action::processGeneric (SGMLToken * t, Error & err) {

  // bogus code to avoid warnings
  if ( !t ) {
    err.addWarning ("processGeneric called with bad argument");
  }

}