/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  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 .
 */
/*
 * CopyAtoms_withBonds.cpp
 *
 *  Created on: Mar 17, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "CopyAtoms_withBonds.hpp"
#include "Bond/bond.hpp"
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "Descriptors/MoleculeIdDescriptor.hpp"
#include "molecule.hpp"
#include "WorldTime.hpp"
void CopyAtoms_withBonds::operator()(const AtomVector &_atoms)
{
  CopyAtoms_Simple::operator()(_atoms);
  // create LookupMap
  LookupMap_t LookupMap = createLookup(_atoms);
  ASSERT( LookupMap.size() == _atoms.size(),
      "CopyAtoms_withBonds::operator() - Lookupmap and original AtomVectors differ in size: "
      +toString(LookupMap.size())+" != "+toString(_atoms.size())+".");
  ASSERT( LookupMap.size() == CopiedAtoms.size(),
      "CopyAtoms_withBonds::operator() - Lookupmap and copied AtomVectors differ in size.: "
      +toString(LookupMap.size())+" != "+toString(CopiedAtoms.size())+".");
  // then go through the bonds of each original atom
  for (AtomVector::const_iterator original_iter = _atoms.begin();
      original_iter != _atoms.end(); ++original_iter) {
    const BondList ListOfBonds = (*original_iter)->getListOfBonds();
    for(BondList::const_iterator iter = ListOfBonds.begin(); iter != ListOfBonds.end(); ++iter) {
      // check both bond partners have been copied
      const bond::ptr Binder = *iter;
      if (*original_iter == Binder->leftatom) {
        LookupMap_t::const_iterator leftiter = LookupMap.find(Binder->leftatom);
        LookupMap_t::const_iterator rightiter = LookupMap.find(Binder->rightatom);
        if ((leftiter !=  LookupMap.end()) && (rightiter !=  LookupMap.end())) {
          // create new bond, copy its properties, and register with both copies
          atom * const LeftAtom = leftiter->second;
          atom * const RightAtom = rightiter->second;
          bond::ptr const NewBond = LeftAtom->addBond(WorldTime::getTime(), RightAtom);
          LOG(3, "DEBUG: Creating copy bond between original " << Binder->leftatom->getId()
              << " and " << Binder->rightatom->getId() << ": " << *NewBond << ".");
          NewBond->setDegree(Binder->getDegree());
          NewBond->Cyclic = Binder->Cyclic;
          NewBond->Type = Binder->Type;
        } else {
          LOG(4, "DEBUG: Skipping bond between original atoms " << Binder->leftatom->getId()
              << " and " << Binder->rightatom->getId() << " as one of them is not present in copied atoms.");
        }
      } else {
        LOG(4, "DEBUG: Skipping bond between original atoms " << Binder->leftatom->getId()
            << " and " << Binder->rightatom->getId() << " due to wrong order.");
      }
    }
  }
  // associate cloned atoms with molecule from original atom
  for (AtomVector::const_iterator original_iter = _atoms.begin();
      original_iter != _atoms.end(); ++original_iter) {
    const atom * const _atom = *original_iter;
    const molecule * const mol = _atom->getMolecule();
    if (mol != NULL) {
      ASSERT( LookupMap.count(_atom),
          "CopyAtoms_withBonds::operator() - atom "+toString(_atom)+" not found in LookupMap.");
      molecule * non_const_mol = World::getInstance().getMolecule(MoleculeById(mol->getId()));
      non_const_mol->AddAtom(LookupMap[_atom]);
    }
  }
  // print copied atoms
  if (DoLog(3))
    for (AtomVector::const_iterator copy_iter = CopiedAtoms.begin();
        copy_iter != CopiedAtoms.end(); ++copy_iter) {
      const BondList ListOfBonds = (*copy_iter)->getListOfBonds();
      LOG(3, "DEBUG: List of bonds for " << **copy_iter << ": " << ListOfBonds << ".");
    }
}