/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * ActionSequence.cpp
 *
 *  Created on: Dec 17, 2009
 *      Author: crueger
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
//#include "CodePatterns/MemDebug.hpp"
#include "Actions/ActionSequence.hpp"
#include "Actions/Action.hpp"
#include "Actions/ActionExceptions.hpp"
#include "UIElements/Dialog.hpp"
#include "CodePatterns/Assert.hpp"
#include 
using namespace MoleCuilder;
ActionSequence::ActionSequence() :
    loop(1)
{}
ActionSequence::ActionSequence(const ActionSequence &_other) :
    loop(1)
{
  // we need to override copy cstor as we have pointer referenced objects
  for(actionSet::const_iterator it=_other.actions.begin(); it!=_other.actions.end(); it++){
    actions.push_back((*it)->clone());
  }
}
ActionSequence::~ActionSequence()
{
  for (actionSet::iterator iter = actions.begin(); !actions.empty(); iter = actions.begin()) {
    delete *iter;
    actions.erase(iter);
  }
}
void ActionSequence::addAction(Action* _action)
{
  // actions are already clone on push as ActionRegistry hands out const refs only
  actions.push_back(_action);
}
bool ActionSequence::removeAction(const std::string &name)
{
  actionSet::iterator it=actions.begin();
  for(; it!=actions.end(); it++){
    if ((*it)->getName() == name) {
      delete *it;
      actions.erase(it);
      break;
    }
  }
  return it!=actions.end();
}
Action* ActionSequence::removeLastAction()
{
  if(actions.empty()) {
    return 0;
  }
  else {
    Action* theAction;
    theAction = actions.back();
    actions.pop_back();
    return theAction;
  }
}
// this method is used outside the ActionModule
// Each action registers itself with the history
Dialog* ActionSequence::fillAllDialogs(Dialog *dialog)
{
  for(actionSet::iterator it=actions.begin(); it!=actions.end(); it++){
    dialog = (*it)->fillDialog(dialog);
  }
  return dialog;
}
// this method is used outside the ActionModule
// Each action registers itself with the history
void ActionSequence::callAll()
{
  for (size_t i=0;icall();
    }
}
// This method is used internally when MakroActions are constructed.
// In this case only the makro Action should be registered and
// handle the states
ActionSequence::stateSet ActionSequence::callAll(bool)
{
  stateSet states;
  for (size_t i=0;iperformCall();
      if (state == Action::failure)
        throw ActionFailureException() << ActionNameString((*it)->getName());
      states.push_back(state);
    }
  return states;
}
ActionSequence::stateSet ActionSequence::undoAll(stateSet states)
{
  ASSERT(canUndo(),"Trying to undo a sequence that contains methods that can't be undone");
  stateSet res;
  actionSet::reverse_iterator actionRit = actions.rbegin();
  stateSet::reverse_iterator stateRit = states.rbegin();
  for(;actionRit!=actions.rend();++actionRit,++stateRit){
    ASSERT(stateRit!=states.rend(),"End of states prematurely reached.");
    if((*actionRit)->shouldUndo()){
      ActionState::ptr newState = (*actionRit)->performUndo(*stateRit);
      // The order of the states has to correspond to the order of the actions
      // this is why we have to add at the beginning
      res.push_front(newState);
    }
    else{
      res.push_front(Action::success);
    }
  }
  return res;
}
ActionSequence::stateSet ActionSequence::redoAll(stateSet states)
{
  stateSet res;
  actionSet::iterator actionIt = actions.begin();
  stateSet::iterator stateIt = states.begin();
  for(;actionIt!=actions.end();++actionIt,++stateIt){
    ASSERT(stateIt!=states.end(),"End of states prematurely reached.");
    if((*actionIt)->shouldUndo()){
      ActionState::ptr newState =(*actionIt)->performRedo(*stateIt);
      res.push_back(newState);
    }
    else{
      res.push_back(Action::success);
    }
  }
  return res;
}
bool ActionSequence::canUndo()
{
  bool canUndo=true;
  for(std::deque::iterator it=actions.begin(); it!=actions.end(); ++it){
    if((*it)->shouldUndo()){
      canUndo &= (*it)->canUndo();
    }
  }
  return canUndo;
}
bool ActionSequence::shouldUndo()
{
  bool shouldUndo = false;
  for(std::deque::iterator it=actions.begin();it!=actions.end();++it){
    shouldUndo |= (*it)->shouldUndo();
  }
  return shouldUndo;
}
void ActionSequence::outputAsCLI(std::ostream &ost) const
{
  for(std::deque::const_iterator it=actions.begin();it!=actions.end();++it){
    (*it)->outputAsCLI(ost);
    ost << " ";
  }
}
void ActionSequence::outputAsPython(std::ostream &ost, const std::string &prefix) const
{
  for(std::deque::const_iterator it=actions.begin();it!=actions.end();++it)
    (*it)->outputAsPython(ost, prefix);
}
void ActionSequence::setOptionValue(const std::string &_token, const std::string &_value)
{
  for(std::deque::const_iterator it=actions.begin();it!=actions.end();++it)
    (*it)->setOptionValue(_token, _value);
}