/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "action.hh"
#include "funcdata.hh"

#include "coreaction.hh"

namespace ghidra {

/// Specify the name, group, and properties of the Action
/// \param f is the collection of property flags
/// \param nm is the Action name
/// \param g is the Action group
Action::Action(uint4 f,const string &nm,const string &g)

{
  flags = f;
  status = status_start;
  breakpoint = 0;
  name = nm;
  basegroup = g;
  count_tests = 0;
  count_apply = 0;
}

/// If enabled, issue a warning that this Action has been applied
/// \param glb is the controlling Architecture
void Action::issueWarning(Architecture *glb)

{
  if ((flags&(rule_warnings_on|rule_warnings_given)) == rule_warnings_on) {
    flags |= rule_warnings_given;
    glb->printMessage("WARNING: Applied action "+name);
  }
}

/// Check if there was an active \e start break point on this action
/// \return true if there was a start breakpoint
bool Action::checkStartBreak(void)

{
  if ((breakpoint&(break_start|tmpbreak_start))!=0) {
    breakpoint &= ~(tmpbreak_start); // Clear breakpoint if temporary
    return true;		// Breakpoint was active
  }
  return false;			// Breakpoint was not active
}

#ifdef OPACTION_DEBUG

/// If this Action matches the given name, enable debugging.
/// \param nm is the Action name to match
/// \return true if debugging was enabled
bool Action::turnOnDebug(const string &nm)

{
  if (nm == name) {
    flags |= rule_debug;
    return true;
  }
  return false;
}

/// If this Action matches the given name, disable debugging.
/// \param nm is the Action name to match
/// \return true if debugging was disabled
bool Action::turnOffDebug(const string &nm)

{
  if (nm == name) {
    flags &= ~rule_debug;
    return true;
  }
  return false;
}
#endif

/// Print out the collected statistics for the Action to stream
/// \param s is the output stream
void Action::printStatistics(ostream &s) const

{
  s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl;
}

/// \param data is the new function \b this Action may affect
void Action::reset(Funcdata &data)

{
  status = status_start;
  flags &= ~rule_warnings_given; // Indicate a warning has not been given yet
}

/// Reset all the counts to zero
void Action::resetStats(void)

{
  count_tests = 0;
  count_apply = 0;
}

/// Check if there was an active \e action breakpoint on this Action
/// \return true if there was an action breakpoint
bool Action::checkActionBreak(void)

{
  if ((breakpoint&(break_action|tmpbreak_action))!=0) {
    breakpoint &= ~(tmpbreak_action); // Clear temporary breakpoint
    return true;		// Breakpoint was active
  }
  return false;			// Breakpoint was not active
}

/// The description is suitable for a console mode listing of actions
/// \param s is the output stream
/// \param num is a starting index to associate with the action (and its sub-actions)
/// \param depth is amount of indent necessary before printing
/// \return the next available index
int4 Action::print(ostream &s,int4 num,int4 depth) const

{
  s << setw(4) << dec << num;
  s << (char *) (((flags&rule_repeatapply)!=0) ? " repeat " : "        ");
  s << (char) (((flags&rule_onceperfunc)!=0) ? '!' : ' ');
  s << (char) (((breakpoint&(break_start|tmpbreak_start))!=0) ? 'S' : ' ');
  s << (char) (((breakpoint&(break_action|tmpbreak_action))!=0) ? 'A' : ' ');
  for(int4 i=0;i<depth*5+2;++i)
    s << ' ';
  s << name;
  return num+1;
}
  
/// This will the Action name and the next step to execute
/// \param s is the output stream
void Action::printState(ostream &s) const

{
  s << name;
  switch(status) {
  case status_repeat:
  case status_breakstarthit:
  case status_start:
    s << " start";
    break;
  case status_mid:
    s << ':';
    break;
  case status_end:
    s << " end";
  }
}

/// A breakpoint can be placed on \b this Action or some sub-action by properly
/// specifying the (sub)action name.
/// \param tp is the type of breakpoint (\e break_start, break_action, etc.)
/// \param specify is the (possibly sub)action to apply the break point to
/// \return true if a breakpoint was successfully set
bool Action::setBreakPoint(uint4 tp,const string &specify)

{
  Action *res = getSubAction(specify);
  if (res != (Action *)0) {
    res->breakpoint |= tp;
    return true;
  }
  Rule *rule = getSubRule(specify);
  if (rule != (Rule *)0) {
    rule->setBreak(tp);
    return true;
  }
  return false;
}

void Action::clearBreakPoints(void)

{
  breakpoint = 0;
}

/// If enabled, a warning will be printed whenever this action applies.
/// The warning can be toggled for \b this Action or some sub-action by
/// specifying its name.
/// \param val is the toggle value for the warning
/// \param specify is the name of the action or sub-action to toggle
/// \return true if the warning was successfully toggled
bool Action::setWarning(bool val,const string &specify)

{
  Action *res = getSubAction(specify);
  if (res != (Action *)0) {
    if (val)
      res->turnOnWarnings();
    else
      res->turnOffWarnings();
    return true;
  }
  Rule *rule = getSubRule(specify);
  if (rule != (Rule *)0) {
    if (val)
      rule->turnOnWarnings();
    else
      rule->turnOffWarnings();
    return true;
  }
  return false;
}

/// An individual Rule can be disabled by name, within \b this Action. It must
/// be specified by a ':' separated name \e path, from the root Action down
/// to the specific Rule.
/// \param specify is the name path
/// \return \b true if the Rule is successfully disabled
bool Action::disableRule(const string &specify)

{
  Rule *rule = getSubRule(specify);
  if (rule != (Rule *)0) {
    rule->setDisable();
    return true;
  }
  return false;
}

/// An individual Rule can be enabled by name, within \b this Action. It must
/// be specified by a ':' separated name \e path, from the root Action down
/// to the specific Rule.
/// \param specify is the name path
/// \return \b true if the Rule is successfully enabled
bool Action::enableRule(const string &specify)

{
  Rule *rule = getSubRule(specify);
  if (rule != (Rule *)0) {
    rule->clearDisable();
    return true;
  }
  return false;
}

/// Pull the next token from a ':' separated list of Action and Rule names
/// \param token will be filled with string up to the next ':'
/// \param remain will be whats left of the list of removing the token and ':'
/// \param is the list to pull the token from
static void next_specifyterm(string &token,string &remain,const string &specify)

{
  string::size_type res = specify.find(':');
  if (res != string::npos) {
    token = specify.substr(0,res);
    remain = specify.substr(res+1);
  }
  else {
    token = specify;
    remain.clear();
  }
}

/// If this Action matches the given name, it is returned. If the
/// name matches a sub-action, this is returned.
/// \param specify is the action name to match
/// \return the matching Action or sub-action
Action *Action::getSubAction(const string &specify)

{
  if (name == specify) return this;
  return (Action *)0;
}

/// Find a Rule, as a component of \b this Action, with the given name.
/// \param specify is the name of the rule
/// \return the matching sub-rule
Rule *Action::getSubRule(const string &specify)

{
  return (Rule *)0;
}

/// Run \b this Action until completion or a breakpoint occurs. Depending
/// on the behavior properties of this instance, the apply() method may get
/// called many times or none.  Generally the number of changes made by
/// the action is returned, but if a breakpoint occurs -1 is returned.
/// A successive call to perform() will "continue" from the break point.
/// \param data is the function being acted on
/// \return the number of changes or -1
int4 Action::perform(Funcdata &data)

{
  int4 res;

  do {
    switch(status) {
    case status_start:
      count = 0;		// No changes made yet by this action
      if (checkStartBreak()) {
	status = status_breakstarthit;
	return -1;		// Indicate partial completion
      }
      count_tests += 1;
    case status_breakstarthit:
    case status_repeat:
      lcount = count;
    case status_mid:
#ifdef OPACTION_DEBUG
      data.debugActivate();
#endif
      res = apply(data);	// Start or continue action
#ifdef OPACTION_DEBUG
      data.debugModPrint(getName());
#endif
      if (res < 0) {		// negative indicates partial completion
	status = status_mid;
	return res;
      }
      else if (lcount < count) { // Action has been applied
	issueWarning(data.getArch());
	count_apply += 1;
	if (checkActionBreak()) {
	  status = status_actionbreak;
	  return -1;		// Indicate action breakpoint
	}
#ifdef OPACTION_DEBUG
	else if (data.debugBreak()) {
	  status = status_actionbreak;
	  data.debugHandleBreak();
	  return -1;
	}
#endif
      }
      break;
    case status_end:
      return 0;			// Rule applied, do not repeat until reset
      break;
    case status_actionbreak:	// Returned -1 last time, but we do not reapply
      break;			// we either repeat, or return our count
    }
    status = status_repeat;
  } while((lcount<count)&&((flags&rule_repeatapply)!=0));

  if ((flags&(rule_onceperfunc|rule_oneactperfunc))!=0) {
    if ((count>0)||((flags&rule_onceperfunc)!=0))
      status = status_end;
    else
      status = status_start;
  }
  else
    status = status_start;

  return count;
}

ActionGroup::~ActionGroup(void)

{
  vector<Action *>::iterator iter;
  
  for(iter=list.begin();iter!=list.end();++iter)
    delete *iter;
}

/// To be used only during the construction of \b this ActionGroup. This routine
/// adds an Action to the end of this group's list.
/// \param ac is the Action to add
void ActionGroup::addAction(Action *ac)

{
  list.push_back(ac);
}

void ActionGroup::clearBreakPoints(void)

{
  vector<Action *>::const_iterator iter;
  for(iter=list.begin();iter!= list.end();++iter)
    (*iter)->clearBreakPoints();
  Action::clearBreakPoints();
}

Action *ActionGroup::clone(const ActionGroupList &grouplist) const

{
  ActionGroup *res = (ActionGroup *)0;
  vector<Action *>::const_iterator iter;
  Action *ac;
  for(iter=list.begin();iter!=list.end();++iter) {
    ac = (*iter)->clone(grouplist);
    if (ac != (Action *)0) {
      if (res == (ActionGroup *)0)
	res = new ActionGroup(flags,getName());
      res->addAction(ac);
    }
  }
  return res;
}

void ActionGroup::reset(Funcdata &data)

{
  vector<Action *>::iterator iter;

  Action::reset(data);
  for(iter=list.begin();iter!=list.end();++iter)
    (*iter)->reset(data);	// Reset each subrule
}

void ActionGroup::resetStats(void)

{
  vector<Action *>::iterator iter;

  Action::resetStats();
  for(iter=list.begin();iter!=list.end();++iter)
    (*iter)->resetStats();
}

int4 ActionGroup::print(ostream &s,int4 num,int4 depth) const

{
  vector<Action *>::const_iterator titer;

  num = Action::print(s,num,depth);
  s << endl;
  for(titer=list.begin();titer!=list.end();++titer) {
    num = (*titer)->print(s,num,depth+1);
    if (state == titer)
      s << "  <-- ";
    s << endl;
  }
  return num;
}

void ActionGroup::printState(ostream &s) const

{
  Action *subact;

  Action::printState(s);
  if (status==status_mid) {
    subact = *state;
    subact->printState(s);
  }
}

Action *ActionGroup::getSubAction(const string &specify)

{
  string token,remain;
  next_specifyterm(token,remain,specify);
  if (name == token) {
    if (remain.empty()) return this;
  }
  else
    remain = specify;		// Still have to match entire specify
  
  vector<Action *>::iterator iter;
  Action *lastaction = (Action *)0;
  int4 matchcount = 0;
  for(iter=list.begin();iter!=list.end();++iter) {
    Action *testaction = (*iter)->getSubAction(remain);
    if (testaction != (Action *)0) {
      lastaction = testaction;
      matchcount += 1;
      if (matchcount > 1) return (Action *)0;
    }
  }
  return lastaction;
}

Rule *ActionGroup::getSubRule(const string &specify)

{
  string token,remain;
  next_specifyterm(token,remain,specify);
  if (name == token) {
    if (remain.empty()) return (Rule *)0;
  }
  else
    remain = specify;		// Still have to match entire specify

  vector<Action *>::iterator iter;
  Rule *lastrule = (Rule *)0;
  int4 matchcount = 0;
  for(iter=list.begin();iter!=list.end();++iter) {
    Rule *testrule = (*iter)->getSubRule(remain);
    if (testrule != (Rule *)0) {
      lastrule = testrule;
      matchcount += 1;
      if (matchcount > 1) return (Rule *)0;
    }
  }
  return lastrule;
}

int4 ActionGroup::apply(Funcdata &data)

{
  int4 res;

  if (status != status_mid)
    state = list.begin();	// Initialize the derived action
  for(;state!=list.end();++state) {
    res = (*state)->perform(data);
    if (res>0) {		// A change was made
      count  += res;
      if (checkActionBreak()) {	// Check if this is an action breakpoint
	++state;
	return -1;
      }
    }
    else if (res<0)		// Partial completion of member
      return -1;		// equates to partial completion of group action
  }

  return 0;			// Indicate successful completion
}

Action *ActionRestartGroup::clone(const ActionGroupList &grouplist) const

{
  ActionGroup *res = (ActionGroup *)0;
  vector<Action *>::const_iterator iter;
  Action *ac;
  for(iter=list.begin();iter!=list.end();++iter) {
    ac = (*iter)->clone(grouplist);
    if (ac != (Action *)0) {
      if (res == (ActionGroup *)0)
	res = new ActionRestartGroup(flags,getName(),maxrestarts);
      res->addAction(ac);
    }
  }
  return res;
}

void ActionRestartGroup::reset(Funcdata &data)

{
  curstart = 0;
  ActionGroup::reset(data);
}

int4 ActionRestartGroup::apply(Funcdata &data)

{
  int4 res;

  if (curstart == -1) return 0;	// Already completed
  for(;;) {
    res = ActionGroup::apply(data);
    if (res != 0) return res;
    if (!data.hasRestartPending()) {
      curstart = -1;
      return 0;
    }
    if (data.isJumptableRecoveryOn()) // Don't restart within jumptable recovery
      return 0;
    curstart += 1;
    if (curstart > maxrestarts) {
      data.warningHeader("Exceeded maximum restarts with more pending");
      curstart = -1;
      return 0;
    }
    data.getArch()->clearAnalysis(&data);

    // Reset everything but ourselves
    vector<Action *>::iterator iter;
    for(iter=list.begin();iter!=list.end();++iter)
      (*iter)->reset(data);	// Reset each subrule
    status = status_start;
  }
}

#ifdef OPACTION_DEBUG
bool ActionGroup::turnOnDebug(const string &nm)

{
  if (Action::turnOnDebug(nm))
    return true;
  vector<Action *>::iterator iter;
  for(iter = list.begin();iter!=list.end();++iter)
    if ((*iter)->turnOnDebug(nm))
      return true;
  return false;
}

bool ActionGroup::turnOffDebug(const string &nm)

{
  if (Action::turnOffDebug(nm))
    return true;
  vector<Action *>::iterator iter;
  for(iter = list.begin();iter!=list.end();++iter)
    if ((*iter)->turnOffDebug(nm))
      return true;
  return false;
}
#endif

void ActionGroup::printStatistics(ostream &s) const

{
  Action::printStatistics(s);
  vector<Action *>::const_iterator iter;
  for(iter = list.begin();iter!=list.end();++iter)
    (*iter)->printStatistics(s);
}

/// \param g is the groupname to which \b this Rule belongs
/// \param fl is the set of properties
/// \param nm is the name of the Rule
Rule::Rule(const string &g,uint4 fl,const string &nm)

{
  flags = fl;
  name = nm;
  breakpoint = 0;
  basegroup = g;
  count_tests = 0;
  count_apply = 0;
}

/// This method is called whenever \b this Rule applies. If warnings have been
/// enabled for the Rule via turnOnWarnings(), this method will print a message
/// indicating the Rule has been applied.  Even with repeat calls, the message
/// will only be printed once (until reset() is called)
/// \param glb is the Architecture holding the console to print to
void Rule::issueWarning(Architecture *glb)

{
  if ((flags&(warnings_on|warnings_given)) == warnings_on) {
    flags |= warnings_given;
    glb->printMessage("WARNING: Applied rule "+name);
  }
}

/// Any state that is specific to a particular function is cleared by this method.
/// This method can be used to initialize a Rule based on a new function it will apply to
/// \param data is the \e new function about to be transformed
void Rule::reset(Funcdata &data)

{
  flags &= ~warnings_given;	// Indicate that warning has not yet been given
}

/// Counts of when this Rule has been attempted/applied are reset to zero.
/// Derived Rules may reset their own statistics.
void Rule::resetStats(void)

{
  count_tests = 0;
  count_apply = 0;
}

#ifdef OPACTION_DEBUG
/// If \b this Rule has the given name, then enable debugging.
/// \param nm is the given name to match
/// \return true if debugging was enabled
bool Rule::turnOnDebug(const string &nm)

{
  if (nm == name) {
    flags |= rule_debug;
    return true;
  }
  return false;
}

/// If \b this Rule has the given name, then disable debugging.
/// \param nm is the given name to match
/// \return true if debugging was disabled
bool Rule::turnOffDebug(const string &nm)

{
  if (nm == name) {
    flags &= ~rule_debug;
    return true;
  }
  return false;
}
#endif

/// Print the accumulated counts associated with applying this Rule to stream.
/// This method is intended for console mode debugging. Derived Rules may
/// override this to display their own statistics.
/// \param s is the output stream
void Rule::printStatistics(ostream &s) const

{
  s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl;
}

/// Populate the given array with all possible OpCodes this Rule might apply to.
/// By default, this method returns all possible OpCodes
/// \param oplist is the array to populate
void Rule::getOpList(vector<uint4> &oplist) const

{
  uint4 i;

  for(i=0;i<CPUI_MAX;++i)
    oplist.push_back(i);
}

/// This method is called every time the Rule successfully applies. If it returns
/// \b true, this indicates to the system that an action breakpoint has occurred.
/// \return true if an action breakpoint should occur because of this Rule
bool Rule::checkActionBreak(void)

{
  if ((breakpoint&(Action::break_action|Action::tmpbreak_action))!=0) {
    breakpoint &= ~(Action::tmpbreak_action); // Clear temporary breakpoint
    return true;		// Breakpoint was active
  }
  return false;			// Breakpoint was not active
}

ActionPool::~ActionPool(void)

{
  vector<Rule *>::iterator iter;

  for(iter=allrules.begin();iter!=allrules.end();++iter)
    delete *iter;
}

/// This method should only be invoked during construction of this ActionPool
/// A single Rule is added to the pool. The Rule's OpCode is inspected by this method.
/// \param rl is the Rule to add
void ActionPool::addRule(Rule *rl)

{
  vector<uint4> oplist;
  vector<uint4>::iterator iter;

  allrules.push_back(rl);

  rl->getOpList(oplist);
  for(iter=oplist.begin();iter!=oplist.end();++iter)
    perop[*iter].push_back(rl);	// Add rule to list for each op it registers for
}

int4 ActionPool::print(ostream &s,int4 num,int4 depth) const

{
  vector<Rule *>::const_iterator iter;
  Rule *rl;
  int4 i;

  num = Action::print(s,num,depth);
  s << endl;
  depth += 1;
  for(iter=allrules.begin();iter!=allrules.end();++iter) {
    rl = *iter;
    s << setw(4) << dec << num;
    s << (char) ( rl->isDisabled() ? 'D' : ' ');
    s << (char) ( ((rl->getBreakPoint()&(break_action|tmpbreak_action))!=0) ? 'A' : ' ');
    for(i=0;i<depth*5+2;++i)
      s << ' ';
    s << rl->getName();
    s << endl;
    num += 1;
  }
  return num;
}

void ActionPool::printState(ostream &s) const

{
  PcodeOp *op;

  Action::printState(s);
  if (status==status_mid) {
    op = (*op_state).second;
    s << ' ' << op->getSeqNum();
  }
}

Rule *ActionPool::getSubRule(const string &specify)

{
  string token,remain;
  next_specifyterm(token,remain,specify);
  if (name == token) {
    if (remain.empty()) return (Rule *)0; // Match, but not a rule
  }
  else
    remain = specify;		// Still have to match entire specify

  vector<Rule *>::iterator iter;
  Rule *lastrule = (Rule *)0;
  int4 matchcount = 0;
  for(iter=allrules.begin();iter!=allrules.end();++iter) {
    Rule *testrule = *iter;
    if (testrule->getName() == remain) {
      lastrule = testrule;
      matchcount += 1;
      if (matchcount > 1) return (Rule *)0;
    }
  }
  return lastrule;
}

/// This method attempts to apply each Rule to the current PcodeOp
/// Action breakpoints are checked if the Rule successfully applies.
/// 0 is returned for no breakpoint, -1 if a breakpoint occurs.
/// If a breakpoint did occur, an additional call to processOp() will
/// pick up where it left off before the breakpoint. The PcodeOp iterator is advanced.
/// \param op is the current PcodeOp
/// \param data is the function being transformed
/// \return 0 if no breakpoint, -1 otherwise
int4 ActionPool::processOp(PcodeOp *op,Funcdata &data)

{
  Rule *rl;
  int4 res;
  uint4 opc;

  if (op->isDead()) {
    op_state++;
    data.opDeadAndGone(op);
    rule_index = 0;
    return 0;
  }
  opc = op->code();
  while(rule_index < perop[opc].size()) {
    rl = perop[opc][rule_index++];
    if (rl->isDisabled()) continue;
#ifdef OPACTION_DEBUG
    data.debugActivate();
#endif
    rl->count_tests += 1;
    res = rl->applyOp(op,data);
#ifdef OPACTION_DEBUG
    data.debugModPrint(rl->getName());
#endif
    if (res>0) {
      rl->count_apply += 1;
      count += res;
      rl->issueWarning(data.getArch()); // Check if we need to issue a warning
      if (rl->checkActionBreak())
        return -1;
#ifdef OPACTION_DEBUG
      if (data.debugBreak()) {
	data.debugHandleBreak();
	return -1;
      }
#endif
      if (op->isDead()) break;
      if (opc != op->code()) {	// Set of rules to apply to this op has changed
        opc = op->code();
        rule_index = 0;		
      }
    }
    else if (opc != op->code()) {
      data.getArch()->printMessage("ERROR: Rule " + rl->getName() + " changed op without returning result of 1!");
      opc = op->code();
      rule_index = 0;	
    }
  }
  op_state++;
  rule_index = 0;

  return 0;
}

int4 ActionPool::apply(Funcdata &data)

{
  if (status != status_mid) {
    op_state = data.beginOpAll();	// Initialize the derived action
    rule_index = 0;
  }
  for(;op_state!=data.endOpAll();)
	  if (0!=processOp((*op_state).second,data)) return -1;

  return 0;			// Indicate successful completion
}

void ActionPool::clearBreakPoints(void)

{
  vector<Rule *>::const_iterator iter;
  for(iter=allrules.begin();iter!=allrules.end();++iter)
    (*iter)->clearBreakPoints();
  Action::clearBreakPoints();
}

Action *ActionPool::clone(const ActionGroupList &grouplist) const

{
  ActionPool *res = (ActionPool *)0;
  vector<Rule *>::const_iterator iter;
  Rule *rl;
  for(iter=allrules.begin();iter!=allrules.end();++iter) {
    rl = (*iter)->clone(grouplist);
    if (rl != (Rule *)0) {
      if (res == (ActionPool *)0)
	res = new ActionPool(flags,getName());
      res->addRule(rl);
    }
  }
  return res;
}

void ActionPool::reset(Funcdata &data)

{
  vector<Rule *>::iterator iter;

  Action::reset(data);
  for(iter=allrules.begin();iter!=allrules.end();++iter)
    (*iter)->reset(data);
}

void ActionPool::resetStats(void)

{
  vector<Rule *>::iterator iter;

  Action::resetStats();
  for(iter=allrules.begin();iter!=allrules.end();++iter)
    (*iter)->resetStats();
}

#ifdef OPACTION_DEBUG
bool ActionPool::turnOnDebug(const string &nm)

{
  vector<Rule *>::iterator iter;

  if (Action::turnOnDebug(nm))
    return true;
  for(iter=allrules.begin();iter!=allrules.end();++iter)
    if ((*iter)->turnOnDebug(nm))
      return true;
  return false;
}

bool ActionPool::turnOffDebug(const string &nm)

{
  vector<Rule *>::iterator iter;

  if (Action::turnOffDebug(nm))
    return true;
  for(iter=allrules.begin();iter!=allrules.end();++iter)
    if ((*iter)->turnOffDebug(nm))
      return true;
  return false;
}
#endif

void ActionPool::printStatistics(ostream &s) const

{
  vector<Rule *>::const_iterator iter;

  Action::printStatistics(s);
  for(iter=allrules.begin();iter!=allrules.end();++iter)
    (*iter)->printStatistics(s);
}

const char ActionDatabase::universalname[] = "universal";

ActionDatabase::~ActionDatabase(void)

{
  map<string,Action *>::iterator iter;
  for(iter = actionmap.begin();iter!=actionmap.end();++iter)
    delete (*iter).second;
}

/// Clear out (possibly altered) root Actions. Reset the default groups.
/// Set the default root action "decompile"
void ActionDatabase::resetDefaults(void)

{
  Action *universalAction = (Action *)0;
  map<string,Action *>::iterator iter;
  iter = actionmap.find(universalname);
  if (iter != actionmap.end())
    universalAction = (*iter).second;
  for(iter = actionmap.begin();iter!=actionmap.end();++iter) {
    Action *curAction = (*iter).second;
    if (curAction != universalAction)
      delete curAction;		// Clear out any old (modified) root actions
  }
  actionmap.clear();
  registerAction(universalname, universalAction);

  buildDefaultGroups();
  setCurrent("decompile");	// The default root action
}

const ActionGroupList &ActionDatabase::getGroup(const string &grp) const

{
  map<string,ActionGroupList>::const_iterator iter;

  iter = groupmap.find(grp);
  if (iter == groupmap.end())
    throw LowlevelError("Action group does not exist: "+grp);
  return (*iter).second;
}

/// The Action is specified by name.  A grouplist must already exist for this name.
/// If the Action doesn't already exist, it will be derived from the \e universal
/// action via this grouplist.
/// \param actname is the name of the \e root Action
Action *ActionDatabase::setCurrent(const string &actname)

{
  currentactname = actname;
  currentact = deriveAction(universalname,actname);
  return currentact;
}


/// A particular group is either added or removed from the grouplist defining
/// a particular \e root Action.  The \e root Action is then (re)derived from the universal
/// \param grp is the name of the \e root Action
/// \param basegrp is name of group (within the grouplist) to toggle
/// \param val is \b true if the group should be added or \b false if it should be removed
/// \return the modified \e root Action
Action *ActionDatabase::toggleAction(const string &grp, const string &basegrp,bool val)

{
  Action *act = getAction(universalname);
  if (val)
    addToGroup(grp,basegrp);
  else
    removeFromGroup(grp,basegrp);
  const ActionGroupList &curgrp(getGroup(grp)); // Group should already exist
  Action *newact = act->clone(curgrp);

  registerAction(grp,newact);

  if (grp == currentactname)
    currentact = newact;

  return newact;
}

/// (Re)set the grouplist for a particular \e root Action.  Do not use this routine
/// to redefine an existing \e root Action.
/// \param grp is the name of the \e root Action
/// \param argv is a list of static char pointers, which must end with a NULL pointer, or a zero length string.
void ActionDatabase::setGroup(const string &grp,const char **argv)

{
  ActionGroupList &curgrp( groupmap[ grp ] );
  curgrp.list.clear();		// Clear out any old members
  for(int4 i=0;;++i) {
    if (argv[i] == (char *)0) break;
    if (argv[i][0] == '\0') break;
    curgrp.list.insert( argv[i] );
  }
  isDefaultGroups = false;
}

/// Copy an existing \e root Action by copying its grouplist, giving it a new name.
/// This is suitable for a copy then modify strategy to create a new \e root Action.
/// Do not use to redefine a \e root Action that has already been instantiated
/// \param oldname is the name of an existing \e root Action
/// \param newname is the name of the copy
void ActionDatabase::cloneGroup(const string &oldname,const string &newname)

{
  const ActionGroupList &curgrp(getGroup(oldname)); // Should already exist
  groupmap[ newname ] = curgrp;	// Copy the group
  isDefaultGroups = false;
}

/// Add a group to the grouplist for a particular \e root Action.
/// Do not use to redefine a \e root Action that has already been instantiated.
/// \param grp is the name of the \e root Action
/// \param basegroup is the group to add
/// \return \b true for a new addition, \b false is the group was already present
bool ActionDatabase::addToGroup(const string &grp, const string &basegroup)

{
  isDefaultGroups = false;
  ActionGroupList &curgrp( groupmap[ grp ] );
  return curgrp.list.insert( basegroup ).second;
}

/// The group is removed from the grouplist of a \e root Action.
/// Do not use to redefine a \e root Action that has already been instantiated.
/// \param grp is the name of the \e root Action
/// \param basegrp is the group to remove
/// \return \b true if the group existed and was removed
bool ActionDatabase::removeFromGroup(const string &grp, const string &basegrp)

{
  isDefaultGroups = false;
  ActionGroupList &curgrp( groupmap[ grp ] );
  return (curgrp.list.erase(basegrp) > 0);
}

/// \param nm is the name of the \e root Action
Action *ActionDatabase::getAction(const string &nm) const

{
  map<string,Action *>::const_iterator iter;
  iter = actionmap.find(nm);
  if (iter == actionmap.end())
    throw LowlevelError("No registered action: "+nm);
  return (*iter).second;
}

/// Internal method for associated a \e root Action name with its Action object.
/// The database takes over memory management of the object.
/// \param nm is the name to register as
/// \param act is the Action object
void ActionDatabase::registerAction(const string &nm,Action *act)

{
  map<string,Action *>::iterator iter;
  iter = actionmap.find(nm);
  if (iter != actionmap.end()) {
    delete (*iter).second;
    (*iter).second = act;
  }
  else {
    actionmap[nm] = act;
  }
}

/// Internal method to build the Action object corresponding to a \e root Action
/// The new Action object is created by selectively cloning components
/// from an existing object based on a grouplist.
/// \param baseaction is the name of the model Action object to derive \e from
/// \param grp is the name of the grouplist steering the clone
Action *ActionDatabase::deriveAction(const string &baseaction, const string &grp)

{
  map<string,Action *>::iterator iter;
  iter = actionmap.find(grp);
  if (iter != actionmap.end())
    return (*iter).second;	// Already derived this action
  
  const ActionGroupList &curgrp(getGroup(grp)); // Group should already exist
  Action *act = getAction(baseaction);
  Action *newact = act->clone( curgrp );

  // Register the action with the name of the group it was derived from
  registerAction(grp,newact);
  return newact;
}

} // End namespace ghidra
