/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2012 University of Bonn. All rights reserved.
 * Copyright (C)  2013-2014 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 .
 */
/*
 * UndoRedoHelpers.cpp
 *
 *  Created on: Apr 5, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
//#include "CodePatterns/MemDebug.hpp"
#include "UndoRedoHelpers.hpp"
#include 
#include 
#include 
#include "Atom/atom.hpp"
#include "molecule.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "Descriptors/MoleculeIdDescriptor.hpp"
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "World.hpp"
#include "WorldTime.hpp"
bool MoleCuilder::AddAtomsFromAtomicInfo(const std::vector &atoms)
{
  size_t i=0;
  for (; i > &mol_atoms)
{
  bool status = true;
  for (std::map< moleculeId_t, std::vector >::const_iterator iter = mol_atoms.begin();
      iter != mol_atoms.end(); ++iter) {
    // re-create the atom
    LOG(3, "DEBUG: Re-adding molecule " << iter->first << ".");
    molecule *mol_Walker = World::getInstance().recreateMolecule(iter->first);
    status &= (mol_Walker != NULL);
    // add all its atoms
    status &= AddAtomsFromAtomicInfo(iter->second);
  }
  if (!status) {
    // remove all molecules again
    for (std::map< moleculeId_t, std::vector >::const_iterator iter = mol_atoms.begin();
        iter != mol_atoms.end(); ++iter) {
      molecule * mol = World::getInstance().getMolecule(MoleculeById(iter->first));
      if (mol != NULL)
        removeAtomsinMolecule(mol);
    }
    // and announce the failure
    return false;
  }
  return true;
}
void MoleCuilder::RemoveAtomsFromAtomicInfo(std::vector &atoms)
{
  BOOST_FOREACH(const AtomicInfo &_atom, atoms) {
    World::getInstance().destroyAtom(_atom.getId());
  }
}
void MoleCuilder::StoreBondInformationFromAtoms(
    const std::vector &atoms,
    std::vector< BondInfo > &bonds)
{
  ASSERT( bonds.empty(),
      "StoreBondInformationFromAtoms() - give bonds vector is not empty.");
  bonds.reserve(atoms.size()*4);
  for (std::vector::const_iterator atomiter = atoms.begin();
      atomiter != atoms.end(); ++atomiter) {
    const BondList & _atom_bonds = (*atomiter)->getListOfBonds();
    for(BondList::const_iterator iter = _atom_bonds.begin(); iter != _atom_bonds.end(); ++iter)
      bonds.push_back( BondInfo(*iter) );
  }
}
bool MoleCuilder::AddBondsFromBondInfo(const std::vector< BondInfo > &bonds)
{
  bool status = true;
  std::vector< BondInfo >::const_iterator iter = bonds.begin();
  for(;iter != bonds.end(); ++iter) {
    if (!(*iter).RecreateBond()) {
      status = false;
      break;
    }
  }
  if (!status) {
    // remove all added bonds again
    for(std::vector< BondInfo >::const_iterator removeiter = bonds.begin();
        removeiter != iter; ++removeiter) {
      removeiter->RemoveBond();
    }
  }
  return status;
}
void MoleCuilder::SetAtomsFromAtomicInfo(const std::vector &_movedatoms)
{
  BOOST_FOREACH( const AtomicInfo &_atominfo, _movedatoms) {
    const atomId_t id = _atominfo.getId();
    atom * const _atom = World::getInstance().getAtom(AtomById(id));
    ASSERT( _atom != NULL,
        "MoleCuilder::SetAtomsFromAtomicInfo() - cannot find atom with id "
        +toString(id)+" in the world.");
    _atominfo.setAtom( *_atom );
  }
}
void MoleCuilder::SelectAtomsFromAtomicInfo(const std::vector &_movedatoms)
{
  BOOST_FOREACH( const AtomicInfo &_atominfo, _movedatoms) {
    const atomId_t id = _atominfo.getId();
    World::getInstance().selectAtom(id);
  }
}
void MoleCuilder::ResetAtomPosition(const std::vector &movedatoms, const std::vector &MovedToVector)
{
  boost::function setter =
      boost::bind(&atom::setPosition, _1, _2);
  ResetByFunction(movedatoms, MovedToVector, setter);
}
void MoleCuilder::ResetAtomVelocity(const std::vector &movedatoms, const std::vector &VelocityVector)
{
  boost::function setter =
      boost::bind(&atom::setAtomicVelocity, _1, _2);
  ResetByFunction(movedatoms, VelocityVector, setter);
}
void MoleCuilder::ResetAtomForce(const std::vector &movedatoms, const std::vector &ForceVector)
{
  boost::function setter =
      boost::bind(&atom::setAtomicForce, _1, _2);
  ResetByFunction(movedatoms, ForceVector, setter);
}
void MoleCuilder::ResetByFunction(
    const std::vector &movedatoms,
    const std::vector &MovedToVector,
    boost::function &setter)
{
  std::vector::const_iterator positer = MovedToVector.begin();
  ASSERT(movedatoms.size() == MovedToVector.size(),
      "MoleCuilder::ResetAtomPosition() -  the number of atoms "
      +toString(movedatoms.size())+" and the number of positions "
      +toString(MovedToVector.size())+" is not the same.");
  BOOST_FOREACH( const AtomicInfo &_atominfo, movedatoms) {
    const atomId_t id = _atominfo.getId();
    atom * const _atom = World::getInstance().getAtom(AtomById(id));
    ASSERT( _atom != NULL,
        "FillSphericalSurfaceAction::performRedo() - cannot find atom with id "
        +toString(id)+" in the world.");
    setter(_atom, *positer );
    ++positer;
  }
}
void MoleCuilder::RemoveMoleculesWithAtomsByIds(const std::vector &ids)
{
  for (std::vector::const_iterator iter = ids.begin();
      iter != ids.end(); ++iter) {
    molecule * mol = World::getInstance().getMolecule(MoleculeById(*iter));
    if (mol != NULL) {
      removeAtomsinMolecule(mol);
      // molecules are automatically removed when empty
    }
  }
}
void MoleCuilder::removeLastStep(const std::vector &_atoms, const unsigned int _step)
{
  for (size_t i=0; i<_atoms.size(); ++i) {
    atom * const _atom = World::getInstance().getAtom(AtomById(_atoms[i]));
    _atom->removeStep(_step);
  }
}
void MoleCuilder::addNewStep(const std::vector &_movedatoms, const unsigned int _step)
{
  for(size_t i=0; i< _movedatoms.size(); ++i) {
    atom * const _atom = World::getInstance().getAtom(AtomById(_movedatoms[i].getId()));
    _atom->UpdateStep(_step);
  }
}
void MoleCuilder::addNewStep(const std::vector &_ids, const unsigned int _step)
{
  for(size_t i=0; i< _ids.size(); ++i) {
    atom * const _atom = World::getInstance().getAtom(AtomById(_ids[i]));
    _atom->UpdateStep(_step);
  }
}
std::vector MoleCuilder::getIdsFromAtomicInfo(const std::vector &movedatoms)
{
  std::vector ids(movedatoms.size(), (size_t)-1);
  std::transform(
      movedatoms.begin(), movedatoms.end(),
      ids.begin(),
      boost::bind(&AtomicInfo::getId, _1));
  return ids;
}