/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  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 .
 */
/*
 * BondAddAction.cpp
 *
 *  Created on: Nov 12, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "World.hpp"
#include "WorldTime.hpp"
#include 
#include 
#include "Actions/BondAction/BondAddAction.hpp"
using namespace MoleCuilder;
// and construct the stuff
#include "BondAddAction.def"
#include "Action_impl_pre.hpp"
/** =========== define the function ====================== */
ActionState::ptr BondAddAction::performCall() {
  // check preconditions
  World& world = World::getInstance();
  if (world.countSelectedAtoms() <= 1) {
    STATUS("There must be at least two atoms selected for BondAction Add.");
    return Action::failure;
  }
  bondPairIds_t bondPairIds;
  for (World::AtomSelectionConstIterator firstiter = world.beginAtomSelection();
      firstiter != world.endAtomSelection(); ++firstiter) {
    for (World::AtomSelectionConstIterator seconditer = firstiter;
        seconditer != world.endAtomSelection(); ++seconditer) {
      if (firstiter == seconditer)
        continue;
      if (!(firstiter->second)->IsBondedTo(WorldTime::getTime(), seconditer->second))
        bondPairIds.push_back(
            std::make_pair((firstiter->second)->getId(), (seconditer->second)->getId()));
    }
  }
  if (bondPairIds.empty()) {
    STATUS("All bonds are already present.");
    return Action::failure;
  }
  // create undo
  BondAddState *UndoState = new BondAddState(bondPairIds, params);
  // execute action
  for (bondPairIds_t::const_iterator iter = bondPairIds.begin();
      iter != bondPairIds.end(); ++iter) {
    atom *firstatom = world.getAtom(AtomById(iter->first));
    atom *secondatom = world.getAtom(AtomById(iter->second));
    ASSERT((firstatom != NULL) && (secondatom != NULL),
        "BondAddAction::performCall() - at least one of the ids "
        +toString(iter->first)+" or "+toString(iter->second)+" is not present.");
    bond::ptr newbond = firstatom->addBond(WorldTime::getTime(), secondatom);
    newbond->setDegree(params.degree.get());
    ASSERT( firstatom->IsBondedTo(WorldTime::getTime(), secondatom),
      "BondAddAction::performCall() - adding bond in between "
      +toString(*firstatom)+" and "+toString(*secondatom)+" failed.");
  }
  return ActionState::ptr(UndoState);
}
ActionState::ptr BondAddAction::performUndo(ActionState::ptr _state) {
  BondAddState *state = assert_cast(_state.get());
  // check whether bond already existed
  World& world = World::getInstance();
  for (bondPairIds_t::const_iterator iter = state->bondPairIds.begin();
      iter != state->bondPairIds.end(); ++iter) {
    atom *firstatom = world.getAtom(AtomById(iter->first));
    atom *secondatom = world.getAtom(AtomById(iter->second));
    ASSERT((firstatom != NULL) && (secondatom != NULL),
        "BondAddAction::performCall() - at least one of the ids "
        +toString(iter->first)+" or "+toString(iter->second)+" is not present.");
    if (firstatom->IsBondedTo(WorldTime::getTime(), secondatom)) {
      firstatom->removeBond(WorldTime::getTime(), secondatom);
    } else {
      ELOG(2, "There is no bond in between "+toString(iter->first)
          +" and "+toString(iter->second)+".");
    }
  }
  return ActionState::ptr(_state);
}
ActionState::ptr BondAddAction::performRedo(ActionState::ptr _state){
  BondAddState *state = assert_cast(_state.get());
  // check whether bond already existed
  World& world = World::getInstance();
  for (bondPairIds_t::const_iterator iter = state->bondPairIds.begin();
      iter != state->bondPairIds.end(); ++iter) {
    atom * const firstatom = world.getAtom(AtomById(iter->first));
    atom * const secondatom = world.getAtom(AtomById(iter->second));
    ASSERT((firstatom != NULL) && (secondatom != NULL),
        "BondAddAction::performCall() - at least one of the ids "
        +toString(iter->first)+" or "+toString(iter->second)+" is not present.");
    if (!firstatom->IsBondedTo(WorldTime::getTime(), secondatom)) {
      firstatom->addBond(WorldTime::getTime(), secondatom);
    } else {
      ELOG(2, "There is already a bond in between "+toString(iter->first)
          +" and "+toString(iter->second)+".");
    }
  }
  return ActionState::ptr(_state);
}
bool BondAddAction::canUndo() {
  return true;
}
bool BondAddAction::shouldUndo() {
  return true;
}
/** =========== end of function ====================== */