/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * Copyright (C)  2013 Frederik Heber. 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 .
 */
/*
 * ActionSequenceUnitTest.cpp
 *
 *  Created on: Dec 17, 2009
 *      Author: crueger
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include 
#include 
#include 
#include "ActionSequenceUnitTest.hpp"
#include "Actions/Action.hpp"
#include "Actions/ActionQueue.hpp"
#include "Actions/ActionSequence.hpp"
#include "Actions/MakroAction.hpp"
#include "stubs/DummyUI.hpp"
using namespace MoleCuilder;
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( ActionSequenceTest );
/* some neccessary stubs for tests */
class canUndoActionStub : public Action
{
public:
  canUndoActionStub(const ActionTrait &_trait):
    Action(_trait){}
  virtual ~canUndoActionStub(){}
  virtual Dialog* fillDialog(Dialog *dialog){
    ASSERT(dialog,"No Dialog given when filling action dialog");
    return dialog;
  }
  virtual ActionState::ptr performCall(){
    return Action::success;
  }
  virtual ActionState::ptr performUndo(ActionState::ptr){
    return Action::success;
  }
  virtual ActionState::ptr performRedo(ActionState::ptr){
    return Action::success;
  }
  virtual bool canUndo(){
    return true;
  }
  virtual bool shouldUndo(){
    return true;
  }
  Action* clone(enum QueryOptions flag = Interactive) const
  {
    return new canUndoActionStub(Traits);
  }
  void prepare(enum QueryOptions flag = Interactive)
  {}
  void outputAsCLI(std::ostream &ost) const
  {}
  void outputAsPython(std::ostream &ost, const std::string &prefix) const
  {}
};
class cannotUndoActionStub : public Action
{
public:
  cannotUndoActionStub(const ActionTrait &_trait) :
    Action(_trait){}
  virtual ~cannotUndoActionStub(){}
  virtual Dialog* fillDialog(Dialog *dialog){
    ASSERT(dialog,"No Dialog given when filling action dialog");
    return dialog;
  }
  virtual ActionState::ptr performCall(){
    return Action::success;
  }
  virtual ActionState::ptr performUndo(ActionState::ptr){
    return Action::success;
  }
  virtual ActionState::ptr performRedo(ActionState::ptr){
    return Action::success;
  }
  virtual bool canUndo(){
    return false;
  }
  virtual bool shouldUndo(){
   return true;
  }
  Action* clone(enum QueryOptions flag = Interactive) const
  {
    return new cannotUndoActionStub(Traits);
  }
  void prepare(enum QueryOptions flag = Interactive)
  {}
  void outputAsCLI(std::ostream &ost) const
  {}
  void outputAsPython(std::ostream &ost, const std::string &prefix) const
  {}
};
class wasCalledActionStub : public Action
{
public:
  wasCalledActionStub(const ActionTrait &_trait) :
      Action(_trait),
      called(false)
  {}
  virtual ~wasCalledActionStub(){}
  virtual Dialog* fillDialog(Dialog *dialog){
    return dialog;
  }
  virtual ActionState::ptr performCall(){
    called = true;
    return Action::success;
  }
  virtual ActionState::ptr performUndo(ActionState::ptr){
    called = false;
    return Action::success;
  }
  virtual ActionState::ptr performRedo(ActionState::ptr){
    called = true;
    return Action::success;
  }
  virtual bool canUndo(){
    return true;
  }
  virtual bool shouldUndo(){
    return true;
  }
  Action* clone(enum QueryOptions flag = Interactive) const
  {
    return new wasCalledActionStub(Traits);
  }
  void prepare(enum QueryOptions flag = Interactive)
  {}
  void outputAsCLI(std::ostream &ost) const
  {}
  void outputAsPython(std::ostream &ost, const std::string &prefix) const
  {}
  bool wasCalled(){
    return called;
  }
private:
  bool called;
};
void ActionSequenceTest::setUp(){
  hasDescriptor = false;
  // setup ActionHistory
  ActionQueue::getInstance();
  // TODO: find a way to really reset the factory to a clean state in tear-down
  if(!hasDescriptor){
    UIFactory::registerFactory(new DummyUIFactory::description());
    hasDescriptor = true;
  }
  UIFactory::makeUserInterface("Dummy");
  // create some necessary stubs used in this test
  ActionTrait canUndoTrait("canUndoActionStub");
  ActionTrait cannotUndoTrait("cannotUndoActionStub");
  positive1 = new canUndoActionStub(canUndoTrait);
  positive2 = new canUndoActionStub(canUndoTrait);
  negative1 = new cannotUndoActionStub(cannotUndoTrait);
  negative2 = new cannotUndoActionStub(cannotUndoTrait);
  ActionTrait wasCalledTrait("wasCalledActionStub");
  shouldCall1 = new wasCalledActionStub(wasCalledTrait);
  shouldCall2 = new wasCalledActionStub(wasCalledTrait);
  shouldNotCall1 = new wasCalledActionStub(wasCalledTrait);
  shouldNotCall2 = new wasCalledActionStub(wasCalledTrait);
}
void ActionSequenceTest::tearDown(){
  delete positive1;
  delete positive2;
  delete negative1;
  delete negative2;
  delete shouldCall1;
  delete shouldCall2;
  delete shouldNotCall1;
  delete shouldNotCall2;
  ActionQueue::purgeInstance();
  {
    UIFactory::purgeInstance();
    hasDescriptor = false;
  }
}
void ActionSequenceTest::canUndoTest(){
  // first section:
  {
    // test some combinations
    {
      ActionSequence *sequence = new ActionSequence();
      sequence->addAction(positive1);
      sequence->addAction(positive2);
      CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() );
      delete sequence;
    }
    {
      ActionSequence *sequence = new ActionSequence();
      sequence->addAction(positive1);
      sequence->addAction(negative2);
      CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() );
      delete sequence;
    }
    {
      ActionSequence *sequence = new ActionSequence();
      sequence->addAction(negative1);
      sequence->addAction(positive2);
      CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() );
      delete sequence;
    }
    {
      ActionSequence *sequence = new ActionSequence();
      sequence->addAction(negative1);
      sequence->addAction(negative2);
      CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() );
      delete sequence;
    }
  }
  // second section:
  {
    // empty sequence can be undone
    ActionSequence *sequence = new ActionSequence();
    CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() );
    // if only a positive action is contained it can be undone
    sequence->addAction(positive1);
    CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() );
    // the single negative action should block the process
    sequence->addAction(negative1);
    CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() );
    // after removing the negative action all is well again
    sequence->removeLastAction();
    CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() );
    delete sequence;
  }
}
void ActionSequenceTest::doesCallTest(){
  ActionSequence *sequence = new ActionSequence();
  sequence->addAction(shouldCall1);
  sequence->addAction(shouldCall2);
  sequence->addAction(shouldNotCall1);
  sequence->addAction(shouldNotCall2);
  sequence->removeLastAction();
  sequence->removeLastAction();
  sequence->callAll();
  CPPUNIT_ASSERT_EQUAL(true,shouldCall1->wasCalled());
  CPPUNIT_ASSERT_EQUAL(true,shouldCall2->wasCalled());
  CPPUNIT_ASSERT_EQUAL(false,shouldNotCall1->wasCalled());
  CPPUNIT_ASSERT_EQUAL(false,shouldNotCall2->wasCalled());
  delete sequence;
}
void ActionSequenceTest::doesUndoTest(){
  ActionSequence *sequence = new ActionSequence();
  ActionTrait wasCalledTrait("wasCalledActionStub");
  sequence->addAction(new wasCalledActionStub(wasCalledTrait));
  sequence->addAction(new wasCalledActionStub(wasCalledTrait));
  ActionTrait MakroTrait("Test MakroAction");
  MakroAction act(MakroTrait,*sequence);
  wasCalledActionStub *wasCalled1 =
      static_cast(act.actions.actions.front());
  wasCalledActionStub *wasCalled2 =
      static_cast(act.actions.actions.back());
  act.call();
  CPPUNIT_ASSERT_EQUAL(true,
      static_cast(act.actions.actions.front())->wasCalled());
  CPPUNIT_ASSERT_EQUAL(true,
      static_cast(act.actions.actions.back())->wasCalled());
  ActionQueue::getInstance().undoLast();
  CPPUNIT_ASSERT_EQUAL(false,
      static_cast(act.actions.actions.front())->wasCalled());
  CPPUNIT_ASSERT_EQUAL(false,
      static_cast(act.actions.actions.back())->wasCalled());
  delete sequence;
}