/*
 * 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 .
 */
/*
 * GLWorldScene.cpp
 *
 *  This is based on the Qt3D example "teaservice", specifically parts of teaservice.cpp.
 *
 *  Created on: Aug 17, 2011
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "GLWorldScene.hpp"
#include 
#include 
#include 
#include 
#include 
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_atom.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_bond.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_molecule.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_shape.hpp"
#include "UIElements/Qt4/InstanceBoard/QtObservedInstanceBoard.hpp"
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Log.hpp"
#include 
#include "Actions/SelectionAction/Atoms/AtomByIdAction.hpp"
#include "Actions/SelectionAction/Atoms/NotAtomByIdAction.hpp"
#include "Actions/SelectionAction/Molecules/MoleculeByIdAction.hpp"
#include "Actions/SelectionAction/Molecules/NotMoleculeByIdAction.hpp"
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "Descriptors/MoleculeIdDescriptor.hpp"
#include "Helpers/helpers.hpp"
#include "Shapes/ShapeRegistry.hpp"
#include "molecule.hpp"
#include "World.hpp"
#include 
using namespace MoleCuilder;
// static functions
static void eraseBondNodeParents(
    std::vector &_BondNodeParentMaps,
    const ObservedValue_Index_t &_bondid)
{
  const size_t left_erased = _BondNodeParentMaps[0].left.erase(_bondid);
  const size_t right_erased = _BondNodeParentMaps[1].left.erase(_bondid);
  ASSERT( (left_erased == 1) && (right_erased == 1),
      "eraseBondNodeParents() - could not erase both parents of "+toString(_bondid)+"?");
}
/** Checks whether both parents of the bond are set to GLWorldScene and the
 * bondRemoved() signals was received for this bond.
 *
 * \param _BondNodeParent_Maps - map containing parents
 * \param _ToBeRemovedNodes - set with all bonds that are to be removed
 * \param _bondid id of bond
 * \return true - both parents are assigned to GLWorldScene, false - else
 */
static bool checkBondRemoval(
    const std::vector &_BondNodeParentMaps,
    const GLWorldScene::ToBeRemovedNodes_t &_ToBeRemovedNodes,
    const ObservedValue_Index_t &_bondid)
{
  GLWorldScene::BondNodeParentMap_t::left_const_iterator leftiter =
      _BondNodeParentMaps[0].left.find(_bondid);
  GLWorldScene::BondNodeParentMap_t::left_const_iterator rightiter =
      _BondNodeParentMaps[1].left.find(_bondid);
  ASSERT( leftiter != _BondNodeParentMaps[0].left.end(),
      "checkBondRemoval() - left parent to index "+toString(_bondid)+" missing?");
  ASSERT( rightiter != _BondNodeParentMaps[1].left.end(),
      "checkBondRemoval() - right parent to index "+toString(_bondid)+" missing?");
  return ((leftiter->second == (ObservedValue_Index_t)NULL)
      && (rightiter->second == (ObservedValue_Index_t)NULL)
      && (_ToBeRemovedNodes.count(_bondid)));
}
void removeBondFromScene(
    GLWorldScene::BondNodeMap &_BondsinSceneMap,
    const ObservedValue_Index_t &_bondid)
{
  std::pair iters =
      _BondsinSceneMap.equal_range(_bondid);
  ASSERT( iters.first != iters.second,
      "removeBondFromScene() - could not find bond to id "+toString(_bondid));
  for (GLWorldScene::BondNodeMap::iterator iter = iters.first; iter != iters.second; ++iter) {
    GLMoleculeObject_bond *bondObject = iter->second;
    delete bondObject; // is done by signal from bond itself
    //LOG(4, "INFO: Still present bonds " << BondsinSceneMap << ".");
  }
  _BondsinSceneMap.erase(_bondid);
}
static bool checkAtomRemoval(
    const GLWorldScene::AtomNodeParentMap_t &_AtomNodeParentMap,
    const GLWorldScene::ToBeRemovedNodes_t &_ToBeRemovedNodes,
    const ObservedValue_Index_t &_atomid)
{
  GLWorldScene::AtomNodeParentMap_t::left_const_iterator iter = _AtomNodeParentMap.left.find(_atomid);
  ASSERT( iter != _AtomNodeParentMap.left.end(),
      "checkAtomRemoval() - missing parent entry for "+toString(_atomid));
  return ((iter->second == (ObservedValue_Index_t)NULL) && (_ToBeRemovedNodes.count(_atomid)));
}
// class definitions
GLWorldScene::GLWorldScene(
    QtObservedInstanceBoard * _board,
    QObject *parent) :
  QObject(parent),
  BondNodeParentMaps(std::vector(2)),
  selectionMode(SelectAtom),
  board(_board)
{
  qRegisterMetaType("atomId_t");
  qRegisterMetaType("GLMoleculeObject_bond::SideOfBond");
  int sphereDetails[] = {5, 3, 2, 0};
  int cylinderDetails[] = {16, 8, 6, 3};
  for (int i=0;isetOption(QGLSceneNode::CullBoundingBox, true);
    QGLBuilder cylinderBuilder;
    cylinderBuilder << QGLCylinder(.25,.25,1.0,cylinderDetails[i]);
    GLMoleculeObject::meshCylinder[i] = cylinderBuilder.finalizedSceneNode();
    GLMoleculeObject::meshCylinder[i]->setOption(QGLSceneNode::CullBoundingBox, true);
  }
  connect(board, SIGNAL(moleculeInserted(QtObservedMolecule::ptr)),
      this, SLOT(insertMolecule(QtObservedMolecule::ptr)));
  connect(board, SIGNAL(moleculeRemoved(ObservedValue_Index_t)),
      this, SLOT(removeMolecule(ObservedValue_Index_t)));
  connect(board, SIGNAL(atomInserted(QtObservedAtom::ptr)),
      this, SLOT(connectAtom(QtObservedAtom::ptr)), Qt::DirectConnection);
  connect(this, SIGNAL(atomConnected(QtObservedAtom::ptr)),
      this, SLOT(insertAtom(QtObservedAtom::ptr)));
  connect(board, SIGNAL(atomRemoved(ObservedValue_Index_t)),
      this, SLOT(removeAtom(ObservedValue_Index_t)));
  connect(board, SIGNAL(bondInserted(QtObservedBond::ptr)),
      this, SLOT(connectBond(QtObservedBond::ptr)), Qt::DirectConnection);
  connect(this, SIGNAL(bondConnected(QtObservedBond::ptr)),
      this, SLOT(insertBond(QtObservedBond::ptr)));
  connect(board, SIGNAL(bondRemoved(ObservedValue_Index_t)),
      this, SLOT(removeBond(ObservedValue_Index_t)));
//  connect(this, SIGNAL(updated()), this, SLOT(update()));
}
GLWorldScene::~GLWorldScene()
{
  // remove all elements
  GLMoleculeObject::cleanMaterialMap();
}
void GLWorldScene::clickAtom(atomId_t no)
{
   LOG(3, "INFO: GLWorldScene - atom " << no << " has been clicked.");
   const atom * const Walker = const_cast(World::getInstance()).
       getAtom(AtomById(no));
   ASSERT( Walker != NULL,
       "GLWorldScene::clickAtom() - clicked atom has disappeared.");
   if (selectionMode == SelectAtom){
     if (!World::getInstance().isSelected(Walker))
       SelectionAtomById(std::vector(1,no));
     else
       SelectionNotAtomById(std::vector(1,no));
   }else if (selectionMode == SelectMolecule){
     const molecule *mol = Walker->getMolecule();
     ASSERT(mol, "Atom without molecule has been clicked.");
     molids_t ids(1, mol->getId());
     if (!World::getInstance().isSelected(mol))
       SelectionMoleculeById(ids);
     else
       SelectionNotMoleculeById(ids);
   }
   emit clicked(no);
}
/** Prepares adding an atom to the scene
 *
 * We need to listen to moleculeChanged() in order to properly re-parent()
 * this QObject
 *
 * @param _atom atom to connect
 */
void GLWorldScene::connectAtom(QtObservedAtom::ptr _atom)
{
  boost::recursive_mutex::scoped_lock lock(Atom_mutex);
  connect(_atom.get(), SIGNAL(moleculeChanged()), this, SLOT(reparentAtom()));
  // store the object, as we need it on reparenting
  const ObservedValue_Index_t atomid = _atom->getIndex();
#ifndef NDEBUG
  std::pair< ObservedAtoms_t::iterator, bool > inserter =
#endif
      ObservedAtoms.insert( std::make_pair(_atom.get(), _atom) );
  ASSERT( inserter.second,
      "GLWorldScene::connectAtom() - observed atom "+toString(_atom)+" already stored?");
#ifndef NDEBUG
  std::pair< ObservedValueIndexLookup_t::iterator, bool > indexinserter =
#endif
      ObservedAtomIndexLookup.insert( std::make_pair(atomid, _atom.get()) );
  ASSERT( indexinserter.second,
      "GLWorldScene::connectAtom() - observed atom's index "
      +toString(atomid)+" already stored?");
  // and create entry in the parent map
  AtomNodeParentMap.left.insert( std::make_pair(atomid, (ObservedValue_Index_t)NULL) );
  emit atomConnected(_atom);
}
/** Adds an atom to the scene.
 *
 * @param _atom atom to add
 */
void GLWorldScene::insertAtom(QtObservedAtom::ptr _atom)
{
  LOG(3, "INFO: GLWorldScene: Received signal atomInserted for atom "
      << _atom->getAtomIndex());
  boost::recursive_mutex::scoped_lock lock(Atom_mutex);
  const ObservedValue_Index_t atomid = _atom->getIndex();
  QObject *parent = static_cast(this);
  GLMoleculeObject_atom *atomObject = NULL;
  {
    AtomNodeParentMap_t::left_iterator parentiter = AtomNodeParentMap.left.find(atomid);
    ASSERT (parentiter != AtomNodeParentMap.left.end(),
        "GLWorldScene::insertAtom() - parent to atom id "+toString(atomid)+" unknown?");
    const ObservedValue_Index_t parentindex = parentiter->second;
    if (parentindex != (ObservedValue_Index_t)NULL) {
      const MoleculeNodeMap::iterator moliter = MoleculesinSceneMap.find(parentindex);
      if (moliter != MoleculesinSceneMap.end())
        parent = moliter->second;
    }
    atomObject = new GLMoleculeObject_atom(
            GLMoleculeObject::meshSphere,
            parent,
            _atom);
    ASSERT( atomObject != NULL,
        "GLWorldScene::atomInserted - could not create atom object for "
        +toString(_atom->getAtomIndex()));
  }
#ifndef NDEBUG
  const AtomNodeMap::iterator iter = AtomsinSceneMap.find(atomid);
#endif
  ASSERT(iter == AtomsinSceneMap.end(),
      "GLWorldScene::insertAtom - same atom with id "
      +toString(_atom->getAtomIndex())+" added again.");
  AtomsinSceneMap.insert( make_pair(atomid, atomObject) );
  connect (atomObject, SIGNAL(clicked(atomId_t)), this, SLOT(clickAtom(atomId_t)));
  connect (atomObject, SIGNAL(changed()), this, SIGNAL(changed()));
  connect (atomObject, SIGNAL(hoverChanged(GLMoleculeObject *)), this, SIGNAL(changed()));
  connect (atomObject, SIGNAL(hoverChanged(GLMoleculeObject *)), this, SLOT(hoverChangedSignalled(GLMoleculeObject *)));
  emit changed();
  emit changeOccured();
}
template 
void removeStoredObservedValue(
    GLWorldScene::ObservedValueIndexLookup_t &_ObservedValueIndexLookup,
    T &_ObservedValues,
    const ObservedValue_Index_t &_id)
{
  // get QObject* from lookup
  const GLWorldScene::ObservedValueIndexLookup_t::iterator iter =
      _ObservedValueIndexLookup.find(_id);
  ASSERT( iter != _ObservedValueIndexLookup.end(),
      "removeStoredObservedValue() - could not find index "+toString(_id)
      +" to stored observed value.");
  QObject * sender = iter->second;
  const size_t erased = _ObservedValues.erase(sender);
  ASSERT( erased == 1,
      "removeStoredObservedValue() - could not erase stored observed value "
      +toString(_id));
  _ObservedValueIndexLookup.erase(iter);
}
void removeAtomFromScene(
    GLWorldScene::AtomNodeMap &_AtomsinSceneMap,
    const ObservedValue_Index_t &_atomid)
{
  GLWorldScene::AtomNodeMap::iterator iter = _AtomsinSceneMap.find(_atomid);
  ASSERT(iter != _AtomsinSceneMap.end(),
      "removeAtomFromScene() - atom "+toString(_atomid)+" not on display.");
  GLMoleculeObject_atom *atomObject = iter->second;
  _AtomsinSceneMap.erase(iter);
  delete atomObject;
}
void GLWorldScene::checkAndRemoveAtom(const ObservedValue_Index_t &_atomid)
{
  boost::recursive_mutex::scoped_lock lock(Atom_mutex);
  if (checkAtomRemoval(AtomNodeParentMap, ToBeRemovedNodes, _atomid)) {
    LOG(4, "DEBUG: Found parent of to be removed atom as GLWorldScene, removing.");
    removeAtomFromScene(AtomsinSceneMap, _atomid);
    AtomNodeParentMap.left.erase(_atomid);
    ToBeRemovedNodes.erase(_atomid);
    removeStoredObservedValue(ObservedAtomIndexLookup, ObservedAtoms, _atomid);
  }
}
/** Removes an atom.
 *
 * @param _atomid index of the atom that is removed
 */
void GLWorldScene::removeAtom(ObservedValue_Index_t _atomid)
{
  LOG(3, "INFO: GLWorldScene: Received signal atomRemoved for atom "+toString(_atomid)+".");
  // bonds are removed by signal coming from ~bond
  boost::recursive_mutex::scoped_lock lock(Atom_mutex);
  ASSERT( ToBeRemovedNodes.count(_atomid) == 0,
      "GLWorldScene::removeAtom() - removedAtom signal for id "+toString(_atomid)
      +" came in twice?");
  ToBeRemovedNodes.insert( _atomid );
  // remove atoms
  checkAndRemoveAtom( _atomid );
  emit changed();
  emit changeOccured();
}
/** Prepares adding a bond to the scene
 *
 * We need to listen to moleculeChanged() in order to properly re-parent()
 * this QObject
 *
 * @param _bond bond to connect
 */
void GLWorldScene::connectBond(QtObservedBond::ptr _bond)
{
  LOG(3, "INFO: GLWorldScene::connectBond() - connecting to bonds " << _bond->getBondIndex());
  connect(_bond.get(), SIGNAL(leftmoleculeChanged()), this, SLOT(reparentBondLeft()), Qt::QueuedConnection);
  connect(_bond.get(), SIGNAL(rightmoleculeChanged()), this, SLOT(reparentBondRight()), Qt::QueuedConnection);
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  // store the object, as we need it on reparenting
  const ObservedValue_Index_t bondid = _bond->getIndex();
  {
#ifndef NDEBUG
    std::pair< ObservedBonds_t::iterator, bool > inserter =
#endif
        ObservedBonds.insert( std::make_pair(_bond.get(), _bond) );
    ASSERT( inserter.second,
        "GLWorldScene::connectBond() - observed bond "+toString(_bond)+" already stored?");
#ifndef NDEBUG
    std::pair< ObservedValueIndexLookup_t::iterator, bool > indexinserter =
#endif
        ObservedBondIndexLookup.insert( std::make_pair(bondid, _bond.get()) );
    ASSERT( indexinserter.second,
        "GLWorldScene::connectBond() - observed bond's index "
        +toString(bondid)+" already stored?");
  }
  {
    if ((_bond->getLeftAtom() != NULL) && (_bond->getLeftAtom()->getMoleculeRef() != NULL))
      BondNodeParentMaps[0].left.insert(
          std::make_pair(bondid, _bond->getLeftAtom()->getMoleculeRef()->getIndex()) );
    else
      BondNodeParentMaps[0].left.insert(
          std::make_pair(bondid, (ObservedValue_Index_t)NULL) );
    if ((_bond->getRightAtom() != NULL) && (_bond->getRightAtom()->getMoleculeRef() != NULL))
      BondNodeParentMaps[1].left.insert(
          std::make_pair(bondid, _bond->getRightAtom()->getMoleculeRef()->getIndex()) );
    else
      BondNodeParentMaps[1].left.insert(
          std::make_pair(bondid, (ObservedValue_Index_t)NULL) );
  }
  emit bondConnected(_bond);
}
/** Adds a bond to the scene.
 *
 * @param _bond bond to add
 */
void GLWorldScene::insertBond(QtObservedBond::ptr _bond)
{
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  const std::vector< GLMoleculeObject_bond::SideOfBond > bondsides =
      boost::assign::list_of
          (GLMoleculeObject_bond::left)
          (GLMoleculeObject_bond::right);
  LOG(3, "INFO: GLWorldScene::insertBond() - Adding bonds " << _bond->getBondIndex());
  const ObservedValue_Index_t bondid = _bond->getIndex();
#ifdef NDEBUG
  BondNodeMap::iterator iter = BondsinSceneMap.find(bondid);
  ASSERT( iter == BondsinSceneMap.end(),
    "GLWorldScene::insertBond() - bond "+toString(bondid)+" is already known.");
#endif
  {
    for (size_t i=0;i<2;++i) {
      BondNodeParentMap_t::left_iterator parentiter = BondNodeParentMaps[i].left.find(bondid);
      ASSERT (parentiter != BondNodeParentMaps[i].left.end(),
          "GLWorldScene::insertBond() - parent to bond id "+toString(bondid)+" unknown?");
      QObject *parent = this;
      if (parentiter->second != (ObservedValue_Index_t)NULL) {
        const MoleculeNodeMap::iterator moliter = MoleculesinSceneMap.find(parentiter->second);
        if (moliter != MoleculesinSceneMap.end())
          parent = moliter->second;
      }
      GLMoleculeObject_bond *bondObject = new GLMoleculeObject_bond(
              GLMoleculeObject::meshCylinder,
              parent,
              _bond,
              bondsides[i]);
      connect (bondObject, SIGNAL(changed()), this, SIGNAL(changed()));
      BondsinSceneMap.insert( std::make_pair(bondid, bondObject) );
    }
  }
  emit changed();
  emit changeOccured();
}
void GLWorldScene::checkAndRemoveBond(const ObservedValue_Index_t &_bondid)
{
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  if (checkBondRemoval(BondNodeParentMaps, ToBeRemovedNodes, _bondid)) {
    LOG(3, "DEBUG: Found both parents of to be removed bond " << _bondid << " as GLWorldScene, removing.");
    removeBondFromScene(BondsinSceneMap, _bondid);
    removeStoredObservedValue(ObservedBondIndexLookup, ObservedBonds, _bondid);
    eraseBondNodeParents(BondNodeParentMaps, _bondid);
    ToBeRemovedNodes.erase(_bondid);
  }
}
/** Removes a bond.
 *
 * @param _bondid id of bond to remove
 */
void GLWorldScene::removeBond(ObservedValue_Index_t _bondid)
{
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  LOG(3, "INFO: GLWorldScene::removedBond() - got bondRemoved signal from board for id " << _bondid);
  // check current state of associated molecules: We won't receive any
  // moleculeChanged() here anymore, as this QtObservedBond is already
  // disconnected from its bond object
  {
    const ObservedValueIndexLookup_t::const_iterator objiter = ObservedBondIndexLookup.find(_bondid);
    ASSERT( objiter != ObservedBondIndexLookup.end(),
        "GLWorldScene::removeBond() - bond's index "+toString(_bondid)
        +" to be removed not stored yet?");
    QObject *obj = objiter->second;
    const ObservedBonds_t::const_iterator observediter = ObservedBonds.find(obj);
    ASSERT( observediter != ObservedBonds.end(),
        "GLWorldScene::removeBond() - bond "+toString(_bondid)
        +" to be removed not stored yet??");
    const QtObservedBond::ptr &_bond = observediter->second;
    if (_bond->getLeftMoleculeIndex() != (moleculeId_t)-1) {
      BondNodeParentMaps[0].left.erase(_bondid);
      BondNodeParentMaps[0].left.insert( std::make_pair( _bondid, (ObservedValue_Index_t)NULL) );
      resetParent(getBondInScene(_bondid, GLMoleculeObject_bond::left), NULL);
    }
    if (_bond->getRightMoleculeIndex() != (moleculeId_t)-1) {
      BondNodeParentMaps[1].left.erase(_bondid);
      BondNodeParentMaps[1].left.insert( std::make_pair( _bondid, (ObservedValue_Index_t)NULL) );
      resetParent(getBondInScene(_bondid, GLMoleculeObject_bond::right), NULL);
    }
  }
  ASSERT( ToBeRemovedNodes.find(_bondid) == ToBeRemovedNodes.end(),
      "GLWorldScene::removeBond() - bondRemoved for "+toString(_bondid)+" obtained twice?");
  ToBeRemovedNodes.insert( _bondid );
  // check whether node has still non-NULL parents, otherwise remove completely
  checkAndRemoveBond( _bondid );
  emit changed();
  emit changeOccured();
}
void GLWorldScene::hoverChangedSignalled(GLMoleculeObject *ob)
{
  boost::recursive_mutex::scoped_lock lock(Atom_mutex);
  // Find the atom, ob corresponds to.
  hoverAtomId = -1;
  GLMoleculeObject_atom *atomObject = dynamic_cast(ob);
  if (atomObject){
    for (AtomNodeMap::iterator iter = AtomsinSceneMap.begin();iter != AtomsinSceneMap.end(); ++ iter){
      if (iter->second == atomObject)
        hoverAtomId = iter->second->objectId();
    }
    // Propagate signal.
    emit hoverChanged(hoverAtomId);
  } else {
    // Find the atom, ob corresponds to.
    GLMoleculeObject_molecule *moleculeObject = dynamic_cast(ob);
    if (moleculeObject){
      // Propagate signal.
      emit hoverChanged(moleculeObject->objectId(), 0);
    }
  }
}
void GLWorldScene::clickMolecule(moleculeId_t no)
{
   LOG(3, "INFO: GLMoleculeObject_molecule - mol " << no << " has been clicked.");
   const molecule * const mol= const_cast(World::getInstance()).
       getMolecule(MoleculeById(no));
   ASSERT(mol, "Atom without molecule has been clicked.");
   molids_t ids(1, mol->getId());
   if (!World::getInstance().isSelected(mol))
     SelectionMoleculeById(ids);
   else
     SelectionNotMoleculeById(ids);
   emit clicked(no);
}
/** Inserts a molecule into the scene.
 *
 * @param _mol molecule to insert
 */
void GLWorldScene::insertMolecule(QtObservedMolecule::ptr _mol)
{
  const ObservedValue_Index_t molid = _mol->getIndex();
  LOG(3, "INFO: GLWorldScene: Received signal moleculeInserted for molecule "
      << _mol->getMolIndex());
  MoleculeNodeMap::const_iterator iter = MoleculesinSceneMap.find(molid);
  ASSERT( iter == MoleculesinSceneMap.end(),
      "GLWorldScene::insertMolecule() - molecule's id "+toString(molid)
      +" already present.");
  // add new object
  LOG(1, "DEBUG: Adding GLMoleculeObject_molecule to id " << molid);
  GLMoleculeObject_molecule *molObject =
      new GLMoleculeObject_molecule(
          GLMoleculeObject::meshEmpty,
          this,
          _mol);
  ASSERT( molObject != NULL,
      "GLWorldScene::insertMolecule - could not create molecule object for "
      +toString(molid));
#ifndef NDEBUG
  foreach (QObject *obj, molObject->children()) {
    GLMoleculeObject *meshobj = qobject_cast(obj);
    ASSERT( meshobj == NULL,
        "GLWorldScene::insertMolecule() - there are already atoms or bonds attached to a to molecule.");
  }
#endif
  // check all atoms for not yet assigned parents
  {
    boost::recursive_mutex::scoped_lock lock(Atom_mutex);
    std::pair iters =
        AtomNodeParentMap.right.equal_range(molid);
    for (AtomNodeParentMap_t::right_const_iterator iter = iters.first;
        iter != iters.second; ++iter) {
      AtomNodeMap::const_iterator atomiter = AtomsinSceneMap.find(iter->second);
      if (atomiter != AtomsinSceneMap.end())
        resetParent(atomiter->second, molObject);
    }
  }
  // check all bonds for not yet assigned parents
  {
    boost::recursive_mutex::scoped_lock lock(Bond_mutex);
    for (size_t i=0;i<2;++i) {
      std::pair iters =
          BondNodeParentMaps[i].right.equal_range(molid);
      for (BondNodeParentMap_t::right_const_iterator iter = iters.first;
          iter != iters.second; ++iter) {
        BondNodeMap::const_iterator bonditer = BondsinSceneMap.find(iter->second);
        if (bonditer != BondsinSceneMap.end())
          resetParent(bonditer->second, molObject);
      }
    }
  }
#ifndef NDEBUG
  std::pair inserter =
#endif
  MoleculesinSceneMap.insert( make_pair(molid, molObject) );
  ASSERT(inserter.second,
      "GLWorldScene::insertMolecule() - molecule "+toString(_mol->getMolIndex())
      +" already present in scene.");
  connect (molObject, SIGNAL(changed()), this, SIGNAL(changed()));
  connect (molObject, SIGNAL(changeOccured()), this, SIGNAL(changeOccured()));
  emit changed();
  emit changeOccured();
}
/** Removes a molecule from the scene.
 *
 * @param _molid index of the molecule to remove
 */
void GLWorldScene::removeMolecule(ObservedValue_Index_t _molid)
{
  LOG(3, "INFO: GLWorldScene: Received signal moleculeRemoved for molecule "+toString(_molid)+".");
  MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_molid);
  ASSERT ( iter != MoleculesinSceneMap.end(),
      "GLWorldScene::removeMolecule() - to be removed molecule "+toString(_molid)
      +" is already gone.");
  GLMoleculeObject_molecule *molObject = iter->second;
  // check for any atoms and bonds still attached to it
#ifndef NDEBUG
  foreach (QObject *obj, molObject->children()) {
    GLMoleculeObject *meshobj = qobject_cast(obj);
    ASSERT( meshobj == NULL,
        "GLWorldScene::removeMolecule() - there are still atoms or bonds attached to a to molecule.");
  }
#endif
  // finally, remove molecule
  delete molObject;
  MoleculesinSceneMap.erase(iter);
  emit changed();
  emit changeOccured();
}
void GLWorldScene::moleculesVisibilityChanged(ObservedValue_Index_t _id, bool _visible)
{
  MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_id);
  ASSERT( iter != MoleculesinSceneMap.end(),
      "GLWorldScene::moleculeInserted() - molecule's id "
      +toString(board->getMoleculeIdToIndex(_id))+" is unknown.");
  GLMoleculeObject_molecule *molObject = iter->second;
  molObject->setVisible(_visible);
  emit changed();
  emit changeOccured();
}
/** This converts safely index into a GLMoleculeObject_molecule.
 *
 * \param _MoleculesinSceneMap all present molecules
 * \param _molid index to look for
 * \return MolObject or NULL when not found
 */
GLMoleculeObject_molecule *GLWorldScene::getMoleculeObject(
    const ObservedValue_Index_t _molid) const
{
  const MoleculeNodeMap::const_iterator moliter = MoleculesinSceneMap.find(_molid);
  if (moliter != MoleculesinSceneMap.end())
    return moliter->second;
  else
    return NULL;
}
/** Changes the parent of an object in the scene.
 *
 * \param _id index of the object whose parent to change
 * \param _ob new parent
 */
void GLWorldScene::reparentAtom()
{
  boost::recursive_mutex::scoped_lock lock(Atom_mutex);
  QObject * origin = sender();
  if (origin == NULL) {
    ELOG(1, "Got reparentAtom() with sender being NULL.");
    return;
  }
  ObservedAtoms_t::const_iterator iter = ObservedAtoms.find(origin);
  ASSERT( iter != ObservedAtoms.end(),
      "GLWorldScene::reparentAtom() - atom's "+toString(origin)+" is no longer stored?");
  QtObservedAtom::ptr walker = iter->second;
  const ObservedValue_Index_t walkerid = walker->getIndex();
  LOG(4, "DEBUG: GLWorldScene: Received signal moleculeChanged for atom "+toString(walkerid)+".");
  AtomNodeParentMap_t::left_iterator parentiter = AtomNodeParentMap.left.find(walkerid);
  ASSERT( parentiter != AtomNodeParentMap.left.end(),
      "GLWorldScene::reparentAtom() - could not find object to id "+toString(walkerid));
  // change parent entry
  AtomNodeParentMap.left.erase(parentiter);
  if (walker->getMoleculeRef() != NULL)
    AtomNodeParentMap.left.insert( std::make_pair(walkerid, walker->getMoleculeRef()->getIndex()) );
  else
    AtomNodeParentMap.left.insert( std::make_pair(walkerid, (ObservedValue_Index_t)NULL) );
  parentiter = AtomNodeParentMap.left.find(walkerid);
  const AtomNodeMap::iterator atomiter = AtomsinSceneMap.find(walkerid);
  if (atomiter != AtomsinSceneMap.end())
    resetParent(atomiter->second, getMoleculeObject(parentiter->second));
  // else atom does not yet exist
  // check whether node is still shown, otherwise remove completely
  checkAndRemoveAtom( walkerid );
}
/** Changes the parent of an left-side bond in the scene.
 *
 */
void GLWorldScene::reparentBondLeft()
{
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  QObject * origin = sender();
  if (origin == NULL) {
    ELOG(1, "Got reparentBondLeft() with sender being NULL.");
    return;
  }
  ObservedBonds_t::const_iterator iter = ObservedBonds.find(origin);
  ASSERT( iter != ObservedBonds.end(),
      "GLWorldScene::reparentBondLeft() - bond "+toString(origin)+" is no longer stored?");
  QtObservedBond::ptr bond = iter->second;
  LOG(3, "INFO: GLWorldScene::reparentBondLeft() - Reparenting left side of bond "
      << bond->getBondIndex() << ".");
  reparentBond(bond, bond->getLeftAtom(), GLMoleculeObject_bond::left);
  // check whether node is still shown, otherwise remove completely
  checkAndRemoveBond( bond->getIndex() );
}
/** Changes the parent of an right-side bond in the scene.
 *
 */
void GLWorldScene::reparentBondRight()
{
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  QObject * origin = sender();
  if (origin == NULL) {
    ELOG(1, "Got reparentBondRight() with sender being NULL.");
    return;
  }
  ObservedBonds_t::const_iterator iter = ObservedBonds.find(origin);
  ASSERT( iter != ObservedBonds.end(),
      "GLWorldScene::reparentBondRight() - bond "+toString(origin)+" is no longer stored?");
  QtObservedBond::ptr bond = iter->second;
  LOG(3, "INFO: GLWorldScene::reparentBondRight() - Reparenting right side of bond "
      << bond->getBondIndex() << ".");
  reparentBond(bond, bond->getRightAtom(), GLMoleculeObject_bond::right);
  // check whether node is still shown, otherwise remove completely
  checkAndRemoveBond( bond->getIndex() );
}
GLMoleculeObject_bond *GLWorldScene::getBondInScene(
    const ObservedValue_Index_t _bondid,
    GLMoleculeObject_bond::SideOfBond _side) const
{
  std::pair iters =
      BondsinSceneMap.equal_range(_bondid);
  ASSERT( std::distance(iters.first, iters.second) >= 1,
      "GLWorldScene::getBondInScene() - not at least one bond of id "
      +toString(_bondid)+" present in scene.");
  for (GLWorldScene::BondNodeMap::const_iterator bonditer = iters.first;
      bonditer != iters.second; ++bonditer) {
    if (bonditer->second->BondSide == _side)
      return bonditer->second;
  }
  return NULL;
}
/** Changes the parent of an object in the scene.
 *
 * \param _atom atom of bond whose molecule we are associated to
 * \param _side side of bond
 */
void GLWorldScene::reparentBond(
    const QtObservedBond::ptr _bond,
    const QtObservedAtom::ptr _atom,
    const GLMoleculeObject_bond::SideOfBond _side)
{
  boost::recursive_mutex::scoped_lock lock(Bond_mutex);
  const size_t dim = (_side == GLMoleculeObject_bond::left) ? 0 : 1;
  const ObservedValue_Index_t bondid = _bond->getIndex();
  BondNodeParentMap_t::left_iterator parentiter = BondNodeParentMaps[dim].left.find(bondid);
  ASSERT( parentiter != BondNodeParentMaps[dim].left.end(),
      "GLWorldScene::reparentBond() - could not find object to id "+toString(bondid));
  // change parent entry
  BondNodeParentMaps[dim].left.erase(bondid);
  if ((_atom != NULL) && (_atom->getMoleculeRef() != NULL))
    BondNodeParentMaps[dim].left.insert( std::make_pair( bondid, _atom->getMoleculeRef()->getIndex()));
  else
    BondNodeParentMaps[dim].left.insert( std::make_pair( bondid, (ObservedValue_Index_t)NULL) );
  parentiter = BondNodeParentMaps[dim].left.find(bondid);
  LOG(3, "INFO: GLWorldScene::reparentBond() - Reparented bond "
      << _bond->getBondIndex() << " to " << parentiter->second);
  // reset parent
  resetParent(getBondInScene(bondid, _side), getMoleculeObject(parentiter->second));
}
/** Resets the parent of an GLMoleculeObject.
 *
 * \param _obj object to reparent
 * \param _molid index of parent molecule
 */
void GLWorldScene::resetParent(
    GLMoleculeObject *_obj,
    GLMoleculeObject_molecule *_molobj)
{
  if (_obj != NULL) {
    if (_molobj != NULL) {
      LOG(5, "DEBUG: Resetting parent of " << _obj << " to " << _molobj);
      _obj->setParent(_molobj);
      ASSERT( _obj->parent() == _molobj,
          "GLWorldScene::resetParent() - new parent "+toString(_molobj)+" was not set.");
    } else {
      LOG(5, "DEBUG: Resetting parent of " << _obj << " to " << this);
      _obj->setParent(this);
      ASSERT( _obj->parent() == this,
          "GLWorldScene::resetParent() - new parent "+toString(this)+" was not set.");
    }
  } else
    ELOG(1, "Object to reparent was NULL.");
  // else object does not yet exist
}
/** Adds a shape to the scene.
 *
 */
void GLWorldScene::addShape(const std::string &_name)
{
  Shape * const shape = ShapeRegistry::getInstance().getByName(_name);
  if (shape != NULL) {
    GLMoleculeObject_shape *shapeObject = new GLMoleculeObject_shape(*shape, this);
    ShapeNodeMap::iterator iter = ShapesinSceneMap.find(_name);
    ASSERT(iter == ShapesinSceneMap.end(),
        "GLWorldScene::addShape() - same shape "+_name+" added again.");
    ShapesinSceneMap.insert( make_pair(_name, shapeObject) );
  } else
    ELOG(2, "GLWorldScene::addShape() - shape disappeared before we could draw it.");
  emit changed();
}
void GLWorldScene::removeShape(const std::string &_name)
{
  ShapeNodeMap::iterator iter = ShapesinSceneMap.find(_name);
  ASSERT(iter != ShapesinSceneMap.end(),
      "GLWorldScene::removeShape() - shape "+_name+" not in scene.");
  ShapesinSceneMap.erase(iter);
  delete(iter->second);
  emit changed();
}
void GLWorldScene::updateSelectedShapes()
{
  foreach (QObject *obj, children()) {
    GLMoleculeObject_shape *shapeobj = qobject_cast(obj);
      if (shapeobj){
        shapeobj->enable(ShapeRegistry::getInstance().isSelected(shapeobj->getShape()));
      }
  }
  emit changed();
}
void GLWorldScene::initialize(QGLView *view, QGLPainter *painter) const
{
  // Initialize all of the mesh objects that we have as children.
   foreach (QObject *obj, children()) {
     GLMoleculeObject *meshobj = qobject_cast(obj);
       if (meshobj)
         meshobj->initialize(view, painter);
   }
}
void GLWorldScene::draw(QGLPainter *painter, const QVector4D &cameraPlane) const
{
   // Draw all of the mesh objects that we have as children.
   foreach (QObject *obj, children()) {
     GLMoleculeObject *meshobj = qobject_cast(obj);
       if (meshobj)
         meshobj->draw(painter, cameraPlane);
   }
}
void GLWorldScene::setSelectionMode(SelectionModeType mode)
{
  selectionMode = mode;
  // TODO send update to toolbar
}
void GLWorldScene::setSelectionModeAtom()
{
  setSelectionMode(SelectAtom);
}
void GLWorldScene::setSelectionModeMolecule()
{
  setSelectionMode(SelectMolecule);
}