/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-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 .
 */
/*
 * QtMoleculeList.cpp
 *
 *  Created on: Jan 21, 2010
 *      Author: crueger
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "QtMoleculeList.hpp"
#include 
#include 
#include "UIElements/Views/Qt4/MoleculeList/QtMoleculeItem.hpp"
#include "UIElements/Views/Qt4/MoleculeList/QtMoleculeItemFactory.hpp"
#include "UIElements/Qt4/InstanceBoard/QtObservedInstanceBoard.hpp"
#include 
#include 
#include 
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Observer/Notification.hpp"
#include "Atom/atom.hpp"
#include "Actions/MoleculeAction/ChangeNameAction.hpp"
#include "Actions/SelectionAction/Molecules/PopMoleculesAction.hpp"
#include "Actions/SelectionAction/Molecules/PushMoleculesAction.hpp"
#include "Actions/SelectionAction/Molecules/MoleculeByIdAction.hpp"
#include "Actions/ActionQueue.hpp"
#include "Actions/ActionSequence.hpp"
#include "Actions/ActionTrait.hpp"
#include "Actions/MakroAction.hpp"
#include "Descriptors/MoleculeIdDescriptor.hpp"
#include "Formula.hpp"
#include "molecule.hpp"
using namespace std;
const unsigned int QtMoleculeList::update_times_per_second = 20;
QtMoleculeList::QtMoleculeList(
    QtObservedInstanceBoard *_board,
    QObject *_parent) :
  QStandardItemModel(_parent),
  update_timer(NULL),
  board(_board),
  callback_DirtyItems(boost::bind(&QtMoleculeList::informDirtyState, this, _1, _2, _3))
{
  setColumnCount(QtMoleculeItemFactory::COLUMNCOUNT);
  resetUpdateTimer();
  connect(this,SIGNAL(itemChanged(QStandardItem*)),this,SLOT(moleculeNameChanged(QStandardItem*)));
  connect(this, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(checkForVisibilityChange(QStandardItem*)));
  connect(board, SIGNAL(moleculeInserted(QtObservedMolecule::ptr)), this, SLOT(moleculeInserted(QtObservedMolecule::ptr)));
  connect(board, SIGNAL(moleculeRemoved(const moleculeId_t)), this, SLOT(moleculeRemoved(const moleculeId_t)));
  connect(board, SIGNAL(moleculeIndexChanged(const moleculeId_t,const moleculeId_t)),
      this, SLOT(moleculeIndexChanged(const moleculeId_t, const moleculeId_t)));
}
QtMoleculeList::~QtMoleculeList()
{}
QVariant QtMoleculeList::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (role == Qt::DisplayRole) {
    if (orientation == Qt::Horizontal) {
      if (section < QtMoleculeItem::COLUMNTYPES_MAX)
        return QString(QtMoleculeItemFactory::COLUMNNAMES[section]);
    }
  }
  return QVariant();
}
void QtMoleculeList::moleculeInserted(QtObservedMolecule::ptr _mol)
{
  // get ObservedMolecule from board
  boost::recursive_mutex::scoped_lock lock(listAccessing_mutex);
  newMolecules.push_back( _mol );
}
void QtMoleculeList::moleculeRemoved(const moleculeId_t _id)
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  KilledItemsPerMolecule_t::iterator iter = KilledItemsPerMolecule.find(_id);
  if (iter == KilledItemsPerMolecule.end())
    KilledItemsPerMolecule.insert( std::make_pair(_id, 1));
  else
    ++(iter->second);
  if (iter->second == QtMoleculeItem::COLUMNTYPES_MAX) {
    boost::recursive_mutex::scoped_lock lock(listAccessing_mutex);
    removedMolecules.push_back( _id );
  }
}
template
void exchangeKeys(
    T &_container,
    const moleculeId_t _oldid,
    const moleculeId_t _newid)
{
  typename T::iterator iter = _container.find(_oldid);
  ASSERT(_container.find(_newid) == _container.end(),
      "exchangeKeys() - new id "+toString(_newid)
      +" already exists in container.");
  _container.insert( std::make_pair(_newid, iter->second) );
  _container.erase(iter);
}
template
void exchangeKeysInSet(
    T &_container,
    const moleculeId_t _oldid,
    const moleculeId_t _newid)
{
  typename T::iterator iter = _container.find(_oldid);
  ASSERT(_container.find(_newid) == _container.end(),
      "exchangeKeys() - new id "+toString(_newid)
      +" already exists in container.");
  _container.insert( _newid );
  _container.erase(iter);
}
template
void exchangeKeysOverAllColumns(
    T &_container,
    const moleculeId_t _oldid,
    const moleculeId_t _newid)
{
  for (int i=0;isecond) );
    MoleculeItemBiMap.left.erase(iter);
  }
  exchangeKeys(KilledItemsPerMolecule, _oldid, _newid);
  exchangeKeysOverAllColumns(dirtyMolItems, _oldid, _newid);
  exchangeKeysInSet(visibilityMolItems, _oldid, _newid);
  {
    std::vector::iterator iter =
        std::find(removedMolecules.begin(), removedMolecules.end(), _oldid);
    removedMolecules.erase(iter);
    removedMolecules.push_back(_newid);
  }
  exchangeKeysInSet(toBeMovedItems, _oldid, _newid);
}
bool QtMoleculeList::isMoleculeItemPresent(const moleculeId_t _molid) const
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  MoleculeItemBiMap_t::left_const_iterator iter =
      MoleculeItemBiMap.left.find(_molid);
  return ( iter != MoleculeItemBiMap.left.end());
}
QtMoleculeItem * QtMoleculeList::MoleculeIdToItem(const moleculeId_t _molid) const
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  MoleculeItemBiMap_t::left_const_iterator iter =
      MoleculeItemBiMap.left.find(_molid);
  ASSERT( iter != MoleculeItemBiMap.left.end(),
      "QtMoleculeList::MoleculeIdToItem() - could not find item to id "
      +toString(_molid));
  return iter->second;
}
const moleculeId_t QtMoleculeList::ItemToMoleculeId(const QtMoleculeItem * const _item) const
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  const MoleculeItemBiMap_t::right_const_iterator iter =
      MoleculeItemBiMap.right.find(const_cast(_item));
  if (iter != MoleculeItemBiMap.right.end())
    return iter->second;
  else
    return -1;
}
QtMoleculeItem * QtMoleculeList::getSpecificMoleculeItem(
    const QtMoleculeItem * const _item,
    const enum QtMoleculeItem::COLUMNTYPES _type) const
{
  QStandardItem *parent_item = _item->parent();
  ASSERT( parent_item != NULL,
      "QtMoleculeList::getSpecificMoleculeItem() - parent of molecule item is NULL");
  return static_cast(parent_item->child(_item->index().row(), _type));
}
bool QtMoleculeList::isGroupItemPresent(const std::string &_formula) const
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  FormulaTreeItemBiMap_t::left_const_iterator iter =
      FormulaItemBiMap.left.find(_formula);
  return ( iter != FormulaItemBiMap.left.end());
}
QStandardItem * QtMoleculeList::FormulaToGroupItem(const std::string &_formula) const
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  FormulaTreeItemBiMap_t::left_const_iterator iter =
      FormulaItemBiMap.left.find(_formula);
  ASSERT( iter != FormulaItemBiMap.left.end(),
      "QtMoleculeList::FormulaToGroupItem() - could not find item to formula "
      +toString(_formula));
  return iter->second;
}
const std::string& QtMoleculeList::GroupItemToFormula(const QStandardItem * const _item) const
{
  boost::recursive_mutex::scoped_lock lock(map_mutex);
  static std::string emptystring;
  const FormulaTreeItemBiMap_t::right_const_iterator iter =
      FormulaItemBiMap.right.find(const_cast(_item));
  if (iter != FormulaItemBiMap.right.end())
    return iter->second;
  else
    return emptystring;
}
QStandardItem * QtMoleculeList::getSpecificGroupItem(
    const QStandardItem * const _item,
    const enum QtMoleculeItem::COLUMNTYPES _type) const
{
  return invisibleRootItem()->child(_item->index().row(), _type);
}
const QModelIndex QtMoleculeList::MoleculeIdToIndex(const moleculeId_t _id) const
{
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  QtMoleculeItem * const item = MoleculeIdToItem(_id);
  ASSERT(item != NULL,
      "QtMoleculeList::MoleculeIdToIndex() - could not find item to "
      +toString(_id));
  return indexFromItem(item);
}
const moleculeId_t QtMoleculeList::IndexToMoleculeId(const QModelIndex &_index) const
{
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  QtMoleculeItem * const item = dynamic_cast(itemFromIndex(_index));
  if (item == NULL)
    return -1;
  else
    return ItemToMoleculeId(item);
}
void QtMoleculeList::addGroupItem(
    QStandardItem *&mainitem,
    const std::string &_molecule_formula)
{
  QList groupItems =
      QtMoleculeItemFactory::getInstance().createGroupItems(_molecule_formula);
  mainitem = groupItems.front();
  {
    boost::recursive_mutex::scoped_lock lock(map_mutex);
    FormulaItemBiMap.left.insert( std::make_pair(_molecule_formula, mainitem) );
  }
  invisibleRootItem()->appendRow(groupItems);
}
QList QtMoleculeList::createMoleculeItems(
    QtObservedMolecule::ptr &_ObservedMolecule,
    std::string &_molecule_formula)
{
  QList molItems =
      QtMoleculeItemFactory::getInstance().createMoleculeItems(
          _ObservedMolecule,
          callback_DirtyItems);
  QtMoleculeItem *mol_item = dynamic_cast(molItems.front());
  ASSERT( mol_item != NULL,
      "QtMoleculeList::createMoleculeItems() - item from factory was not a QtMoleculeItem?");
  MoleculeItemBiMap.left.insert( std::make_pair(_ObservedMolecule->getMolIndex(), mol_item) );
  QStandardItem *formulaitem = molItems.at(QtMoleculeItem::FORMULA);
  ASSERT( formulaitem != NULL,
      "QtMoleculeList::createMoleculeItems() - Formula item not created by factory?");
  _molecule_formula = formulaitem->text().toStdString();
  LOG(1, "Adding " << _molecule_formula << " for "
      << _ObservedMolecule->getMolIndex() << " to MoleculeFormulaMap.");
  MoleculeFormulaMap.insert( std::make_pair( _ObservedMolecule->getMolIndex(), _molecule_formula) );
//  LOG(1, "Inserting molecule " << _molid << ": " << _molecule_formula);
  return molItems;
}
std::string QtMoleculeList::addMolecule(QtObservedMolecule::ptr &_ObservedMolecule)
{
  // find group if already in list
  QStandardItem *groupItem = NULL;
  // create molecule items and obtain the molecule's formula
  std::string molecule_formula;
  QList molItems = createMoleculeItems(_ObservedMolecule, molecule_formula);
  // new molecule type -> create new group
  if (!isGroupItemPresent(molecule_formula)){
    // insert new formula entry into visibility
#ifndef NDEBUG
    std::pair< FormulaVisibilityCountMap_t::iterator, bool> visibilityinserter =
#endif
        FormulaVisibilityCountMap.insert(
            std::make_pair( molecule_formula, (unsigned int)0) );
    ASSERT( visibilityinserter.second,
        "QtMoleculeList::refill() - molecule with formula "
        +molecule_formula+" already in FormulaVisibilityCountMap.");
    // create item and place into Map with formula as key
    addGroupItem(groupItem, molecule_formula);
  } else {
    groupItem = FormulaToGroupItem(molecule_formula);
  }
  ASSERT( groupItem != NULL,
      "QtMoleculeList::addMolecule() - item with id "+toString(_ObservedMolecule->getMolIndex())
      +" has no parent?");
  groupItem->appendRow(molItems);
  return molecule_formula;
}
void QtMoleculeList::removeMoleculeItem(QtMoleculeItem * const _item)
{
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  const QModelIndex mol_index = indexFromItem(_item);
  QStandardItem *groupitem = _item->parent();
  const QModelIndex group_index = groupitem->index();
  {
    boost::recursive_mutex::scoped_lock lock(map_mutex);
    MoleculeItemBiMap_t::right_iterator removeiter =
        MoleculeItemBiMap.right.find(_item);
    ASSERT( removeiter != MoleculeItemBiMap.right.end(),
        "QtMoleculeList::removeMoleculeItem() - could not find item in MoleculeBiMap.");
  //  LOG(1, "Erasing molecule " << (removeiter->second));
    {
      MoleculeFormulaMap_t::iterator removeformulaiter =
          MoleculeFormulaMap.find(removeiter->second);
      ASSERT( removeformulaiter != MoleculeFormulaMap.end(),
          "QtMoleculeList::removeMoleculeItem() - could not find id "
          +toString(removeiter->second)+" in MoleculeFormulaMap.");
      LOG(1, "Removing " << removeformulaiter->second << " for "
          << removeformulaiter->first << " from MoleculeFormulaMap.");
      MoleculeFormulaMap.erase( removeformulaiter );
    }
    MoleculeItemBiMap.right.erase(removeiter);
  }
  removeRows(mol_index.row(), 1, group_index);
}
void QtMoleculeList::resetUpdateTimer()
{
  // check timer's presence
  if (update_timer == NULL) {
    update_timer = new QTimer(this);
    connect( update_timer, SIGNAL(timeout()), this, SLOT(checkState()));
  } else
    update_timer->stop();
  // activate timer
  update_timer->start(1000/update_times_per_second);
}
bool QtMoleculeList::areAnyItemsDirty()
{
  // get whether any items are dirty
  boost::recursive_mutex::scoped_lock lock(listAccessing_mutex);
  bool dirty = false;
  dirty |= !dirtyMolItems.empty();
  dirty |= !visibilityMolItems.empty();
  dirty |= !dirtyGroupItems.empty();
  dirty |= !visibilityGroupItems.empty();
  dirty |= !newMolecules.empty();
  dirty |= !removedMolecules.empty();
  dirty |= !toBeMovedItems.empty();
  return dirty;
}
void QtMoleculeList::checkState()
{
  const bool dirty = areAnyItemsDirty();
  // update if required
  if (dirty)
    updateItemStates();
}
void QtMoleculeList::checkForVisibilityChange(QStandardItem* _item)
{
//  qDebug() << "Item changed called.";
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  if (_item->index().column() == QtMoleculeItem::VISIBILITY) {
//    qDebug() << "visibilityItem changed: " << (_item->checkState() ? "checked" : "unchecked");
    boost::recursive_mutex::scoped_lock lock(listAccessing_mutex);
    if ((_item->parent() == NULL) || (_item->parent() == invisibleRootItem()))
      visibilityGroupItems.insert( std::make_pair(
          GroupItemToFormula(_item->parent()), QtMoleculeItem::VISIBILITY) );
    else
      visibilityMolItems.insert(
          static_cast(_item)->getMoleculeId()
          );
  }
}
void QtMoleculeList::setVisibilityForMoleculeItem(QtMoleculeItem* _item)
{
  if (ChangingChildrensVisibility)
    return;
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  const bool visible = _item->checkState();
  const moleculeId_t molid = _item->getMoleculeId();
  std::string molecule_formula("illegal");
  {
    boost::recursive_mutex::scoped_lock lock(map_mutex);
    MoleculeFormulaMap_t::const_iterator formulaiter =
        MoleculeFormulaMap.find(molid);
    ASSERT( formulaiter != MoleculeFormulaMap.end(),
        "QtMoleculeList::setVisibilityForMoleculeItem() - formula of molecule "
        +toString(molid)+" unknown.");
    molecule_formula = formulaiter->second;
  }
  ASSERT( FormulaVisibilityCountMap.count(molecule_formula) != 0,
        "QtMoleculeList::setVisibilityForMoleculeItem() - molecule with formula " +molecule_formula
        +" is not present in FormulaVisibilityCountMap.");
  // get parent
  QStandardItem *groupItem = _item->parent();
  QStandardItem *visgroupItem = getSpecificGroupItem(groupItem, QtMoleculeItem::VISIBILITY);
  ASSERT( groupItem != NULL,
      "QtMoleculeList::setVisibilityForMoleculeItem() - item with id "
      +toString(_item->getMoleculeId())+" has not parent?");
  // check whether we have to set the group item
  ChangingChildrensVisibility = true;
  if (visible) {
    ++(FormulaVisibilityCountMap[molecule_formula]);
    // compare with occurence/total number of molecules
    if (FormulaVisibilityCountMap[molecule_formula] ==
        (unsigned int)(groupItem->rowCount()))
      visgroupItem->setCheckState(Qt::Checked);
  } else {
    --(FormulaVisibilityCountMap[molecule_formula]);
    // none selected anymore?
    if (FormulaVisibilityCountMap[molecule_formula] == 0)
      visgroupItem->setCheckState(Qt::Unchecked);
  }
  ChangingChildrensVisibility = false;
  emit moleculesVisibilityChanged(_item->getMoleculeId(), visible);
}
void QtMoleculeList::setVisibilityForGroupItem(QStandardItem* _item)
{
  if (ChangingChildrensVisibility)
    return;
  ChangingChildrensVisibility = true;
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  // go through all children, but don't enter for groupItem once more
  const bool visible = _item->checkState();
  QStandardItem *groupitem = getSpecificGroupItem(_item, QtMoleculeItem::NAME);
  for (int i=0;irowCount();++i) {
    QtMoleculeItem *molItem = dynamic_cast(
        groupitem->child(i, QtMoleculeItem::VISIBILITY));
    if (molItem->checkState() != visible) {
      molItem->setCheckState(visible ? Qt::Checked : Qt::Unchecked);
      // emit signal
      emit moleculesVisibilityChanged(molItem->getMoleculeId(), visible);
    }
  }
  // set current number of visible children
  const std::string molecule_formula =
      GroupItemToFormula( getSpecificGroupItem(_item, QtMoleculeItem::NAME) );
  FormulaVisibilityCountMap_t::iterator countiter =
      FormulaVisibilityCountMap.find(molecule_formula);
  ASSERT( countiter != FormulaVisibilityCountMap.end(),
      "QtMoleculeList::setVisibilityForGroupItem() - molecules "+molecule_formula
      +" have no entry in visibility count map?");
  countiter->second = visible ? groupitem->rowCount() : 0;
  ChangingChildrensVisibility = false;
}
static
MoleCuilder::MakroAction *constructMakroRenameAction(
    MoleCuilder::ActionSequence &sequence,
    const std::string &_new_name,
    const moleculeId_t _molid
    )
{
  MoleCuilder::ActionQueue &AQ = MoleCuilder::ActionQueue::getInstance();
  MoleCuilder::ActionTrait trait("change-single-molecule-name");
  sequence.addAction(AQ.getActionByName("push-molecule-selection").clone(MoleCuilder::Action::NonInteractive));
  MoleCuilder::Action * const selectaction =
      AQ.getActionByName("select-molecule-by-id").clone(MoleCuilder::Action::NonInteractive);
  {
    std::stringstream molid_string;
    molid_string << toString(_molid);
    selectaction->setOptionValue("select-molecule-by-id", molid_string.str());
  }
  sequence.addAction(selectaction);
  MoleCuilder::Action * const changeaction =
      AQ.getActionByName("change-molname").clone(MoleCuilder::Action::NonInteractive);
  changeaction->setOptionValue("change-molname", _new_name);
  sequence.addAction(changeaction);
  sequence.addAction(AQ.getActionByName("pop-molecule-selection").clone(MoleCuilder::Action::NonInteractive));
  MoleCuilder::MakroAction* makroaction =
      new MoleCuilder::MakroAction(trait, sequence);
  return makroaction;
}
void QtMoleculeList::moleculeNameChanged(QStandardItem* item)
{
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  // obtain molecule id
  if ( item->index().column() == QtMoleculeItem::NAME) {
    QtMoleculeItem *molitem = assert_cast(item);
    MoleculeItemBiMap_t::right_const_iterator iter = MoleculeItemBiMap.right.find(molitem);
    ASSERT( iter != MoleculeItemBiMap.right.end(),
        "QtMoleculeList::moleculeChanged() - name of unknown molecule changed.");
    const moleculeId_t molid = iter->second;
    // change the name
    molecule * const mol = World::getInstance().getMolecule(MoleculeById(molid));
    std::string cellValue = item->text().toStdString();
    if ((mol->getName() != cellValue) && (!cellValue.empty())) {
      // create actions such that we may undo
      static MoleCuilder::ActionSequence sequence;
      MoleCuilder::MakroAction *makroaction =
          constructMakroRenameAction(sequence, cellValue, molid);
      MoleCuilder::ActionQueue &AQ = MoleCuilder::ActionQueue::getInstance();
      AQ.registerAction(makroaction);
      AQ.queueAction("change-single-molecule-name", MoleCuilder::Action::NonInteractive);
    } else if(cellValue=="") {
      item->setText(QString(mol->getName().c_str()));
    }
}
}
int QtMoleculeList::setOccurrence(QStandardItem * const _groupitem)
{
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  QModelIndex modelindex = _groupitem->index();
  ASSERT( modelindex.isValid(),
      "QtMoleculeList::setOccurrence() - groupitem not associated to model anymore.");
  const int index = modelindex.row();
  QStandardItem *parent_item =
      _groupitem->parent() == NULL ? invisibleRootItem() : _groupitem->parent();
  ASSERT( parent_item != NULL,
      "QtMoleculeList::setOccurrence() - group item at "+toString(index)
      +" does not have a parent?");
  QStandardItem *occ_item = parent_item->child(index, QtMoleculeItem::OCCURRENCE);
  ASSERT( occ_item != NULL,
      "QtMoleculeList::setOccurrence() - group item at "+toString(index)
      +" does not have an occurrence?");
  const int count = _groupitem->rowCount();
  if (count == 0) {
    // we have to remove the group item completely
    boost::recursive_mutex::scoped_lock lock(map_mutex);
    const std::string molecule_formula = _groupitem->text().toStdString();
    FormulaItemBiMap.left.erase(molecule_formula);
    FormulaVisibilityCountMap.erase(molecule_formula);
    return index;
  } else {
    occ_item->setText(QString::number(count));
    return -1;
  }
}
std::string QtMoleculeList::readdItem(QtMoleculeItem *_molitem)
{
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
  // use takeRows of molecule ..
  QStandardItem *groupitem = _molitem->parent();
  ASSERT( groupitem != NULL,
      "QtMoleculeList::readdItem() - mol item at "+toString(_molitem->index().row())
      +" does not have a groupitem?");
  // get updated formula from the item
  QStandardItem *formulaitem =
      _molitem->parent()->child(_molitem->index().row(), QtMoleculeItem::FORMULA);
  const std::string molecule_formula = formulaitem->text().toStdString();
  QList mol_row = _molitem->parent()->takeRow(_molitem->index().row());
  // ..  and re-add where new formula fits
  if (!isGroupItemPresent(molecule_formula)) {
    // add new group item and formula entry
     addGroupItem(groupitem, molecule_formula);
  } else {
    groupitem = FormulaToGroupItem(molecule_formula);
  }
  ASSERT( groupitem != NULL,
      "QtMoleculeList::readdItem() - failed to create a sensible new groupitem");
  // finally add again
  groupitem->appendRow(mol_row);
  return molecule_formula;
}
void QtMoleculeList::informDirtyState(
    const moleculeId_t _id,
    const QtMoleculeItem::COLUMNTYPES _type,
    const QtMoleculeItem::MoveTypes _movetype)
{
  listAccessing_mutex.lock();
  dirtyMolItems.insert( std::make_pair(_id, _type) );
  listAccessing_mutex.unlock();
  if (_movetype == QtMoleculeItem::NeedsMove) {
    // we have to convert whatever item raised the dirty signal to the first
    // item in the row as otherwise multiple items in the row are selected
    // as to be moved, i.e. the same row is moved multiple times
    listAccessing_mutex.lock();
    toBeMovedItems.insert(_id);
    listAccessing_mutex.unlock();
  }
}
void QtMoleculeList::updateItemStates()
{
  /// copy lists such that new signals for dirty/.. may come in right away
  // TODO: if we had move semantics ...
  listAccessing_mutex.lock();
  list_of_molecule_items_t dirtyMolItems_copy = dirtyMolItems;
  dirtyMolItems.clear();
  list_of_molecules_t visibilityMolItems_copy = visibilityMolItems;
  visibilityMolItems.clear();
  list_of_group_items_t dirtyGroupItems_copy = dirtyGroupItems;
  dirtyGroupItems.clear();
  list_of_group_items_t visibilityGroupItems_copy = visibilityGroupItems;
  visibilityGroupItems.clear();
  std::vector newMolecules_copy = newMolecules;
  newMolecules.clear();
  std::vector removedMolecules_copy = removedMolecules;
  removedMolecules.clear();
  list_of_molecules_t toBeMovedItems_copy = toBeMovedItems;
  toBeMovedItems.clear();
  listAccessing_mutex.unlock();
  // wait till initial refill has been executed
  boost::recursive_mutex::scoped_lock lock(refill_mutex);
//  LOG(1, "Starting update.");
  // remove removedMolecules from other lists.
  for (std::vector::const_iterator removeiter = removedMolecules_copy.begin();
      removeiter != removedMolecules_copy.end(); ++removeiter) {
    for (unsigned int i=0;i< QtMoleculeItem::COLUMNTYPES_MAX; ++i)
        dirtyMolItems_copy.erase( std::make_pair(*removeiter,(QtMoleculeItem::COLUMNTYPES)i) );
    toBeMovedItems_copy.erase(*removeiter);
    visibilityMolItems_copy.erase(*removeiter);
  }
  /// 1a. do the update for each dirty item
  for (list_of_molecule_items_t::const_iterator dirtyiter = dirtyMolItems_copy.begin();
      dirtyiter != dirtyMolItems_copy.end(); ++dirtyiter) {
    if (!isMoleculeItemPresent(dirtyiter->first))
      continue;
    QtMoleculeItem * const mol_item =
        getSpecificMoleculeItem(
            MoleculeIdToItem(dirtyiter->first),
            dirtyiter->second);
//    LOG(1, "Updating item " << mol_item);
    mol_item->updateState();
  }
  /// 1b. do the visibility update for each dirty item
  for (list_of_molecules_t::const_iterator visiter = visibilityMolItems_copy.begin();
      visiter != visibilityMolItems_copy.end(); ++visiter) {
    if (!isMoleculeItemPresent(*visiter))
      continue;
    QtMoleculeItem * const visitem =
      getSpecificMoleculeItem(
          MoleculeIdToItem(*visiter),
          QtMoleculeItem::VISIBILITY );
//    LOG(1, "Updating visibility of item " << visitem);
    setVisibilityForMoleculeItem(visitem);
  }
  /// 2. move all items that need to be moved
  typedef std::set formulas_t;
  formulas_t toBeSetOccurrence;
  for (list_of_molecules_t::const_iterator moveiter = toBeMovedItems_copy.begin();
      moveiter != toBeMovedItems_copy.end(); ++moveiter) {
    boost::recursive_mutex::scoped_lock lock(map_mutex);
//    LOG(1, "Moving item " << molitem);
    MoleculeFormulaMap_t::iterator formulaiter =
        MoleculeFormulaMap.find(*moveiter);
    ASSERT( formulaiter != MoleculeFormulaMap.end(),
        "QtMoleculeList::updateItemStates() - formula of molecule "
        +toString(*moveiter)+" unknown.");
//    LOG(1, "Adding " << formulaiter->second << " to toBeSetOccurrence.");
    toBeSetOccurrence.insert( formulaiter->second );
    if (!isMoleculeItemPresent(*moveiter))
      continue;
    QtMoleculeItem *const molitem = MoleculeIdToItem(*moveiter);
    LOG(1, "Moving item " << molitem);
    // remove from formula<->molecule bimap with old formula
    LOG(1, "Removing " << formulaiter->second << " for " << formulaiter->first << " from MoleculeFormulaMap.");
    MoleculeFormulaMap.erase( formulaiter );
    const std::string formula = readdItem(molitem);
    // and add to formula<->molecule bimap with updated formula
    LOG(1, "Adding " << formula << " for " << *moveiter << " to MoleculeFormulaMap.");
    MoleculeFormulaMap.insert( std::make_pair(*moveiter, formula) );
//      LOG(1, "Adding " << formula << " to toBeSetOccurrence.");
    toBeSetOccurrence.insert( formula );
  }
  /// 3. remove all items whose molecules have been removed
  for (std::vector::const_iterator removeiter = removedMolecules_copy.begin();
      removeiter != removedMolecules_copy.end(); ++removeiter) {
//    LOG(1, "Removing molecule " << *removeiter);
    if (!isMoleculeItemPresent(*removeiter))
      continue;
    QtMoleculeItem *item = MoleculeIdToItem(*removeiter);
    if (item != NULL) {
      const std::string formula = item->parent()->text().toStdString();
//      LOG(1, "Adding " << formula << " to toBeSetOccurrence.");
      toBeSetOccurrence.insert( formula );
      removeMoleculeItem(item);
      KilledItemsPerMolecule.erase( *removeiter );
    }
  }
  // throw out items that we added by an update() while we are in this function
  listAccessing_mutex.lock();
  for (std::vector::const_iterator removeiter = removedMolecules_copy.begin();
      removeiter != removedMolecules_copy.end(); ++removeiter) {
    for (unsigned int i=0;i< QtMoleculeItem::COLUMNTYPES_MAX; ++i)
        dirtyMolItems.erase( std::make_pair(*removeiter,(QtMoleculeItem::COLUMNTYPES)i) );
    toBeMovedItems.erase(*removeiter);
    visibilityMolItems.erase(*removeiter);
  }
  listAccessing_mutex.unlock();
  // after that it is not a problem as items have been removed (hence signOff() was called)
  /// 4. instantiate all new items
  for (std::vector::iterator moliter = newMolecules_copy.begin();
      moliter != newMolecules_copy.end(); ++moliter) {
//   LOG(1, "Adding molecule " << ObservedMolecule->getMoleculeName());
    // check that World knows the molecule still
    const std::string formula = addMolecule(*moliter);
//      LOG(1, "Adding " << formula << " to toBeSetOccurrence.");
    toBeSetOccurrence.insert( formula );
  }
  /// 5a. update the group item's occurrence and visibility
  std::set RowsToRemove;
  for (std::set::const_iterator groupiter = toBeSetOccurrence.begin();
      groupiter != toBeSetOccurrence.end(); ++groupiter) {
//    LOG(1, "Updating group item's occurence " << *groupiter);
    QStandardItem *groupitem = FormulaToGroupItem(*groupiter);
    const int index = setOccurrence(groupitem);
    if (index != -1) {
//      LOG(1, "Removing row of group item " << groupitem);
      RowsToRemove.insert(index);
    }
  }
  toBeSetOccurrence.clear();
  // remove all visibility updates whose row is removed
  for (list_of_group_items_t::iterator visiter = visibilityGroupItems_copy.begin();
      visiter != visibilityGroupItems_copy.end(); ) {
      QStandardItem * const groupitem = FormulaToGroupItem(visiter->first);
    if (RowsToRemove.count(groupitem->index().row()) != 0) {
//      LOG(1, "Removing vis item " << *visiter << " because of removed group item.");
      visibilityGroupItems_copy.erase(visiter++);
    } else
      ++visiter;
  }
  // update visibility of all group items
  for (list_of_group_items_t::iterator visiter = visibilityGroupItems_copy.begin();
      visiter != visibilityGroupItems_copy.end(); ++visiter) {
//    LOG(1, "Updating visibility of item " << *visiter);
    QStandardItem * const groupitem =
        getSpecificGroupItem(FormulaToGroupItem(visiter->first),
        visiter->second);
    setVisibilityForGroupItem(groupitem);
  }
  /// 5b. remove all rows with 0 occurrence starting from last
  for (std::set::reverse_iterator riter = RowsToRemove.rbegin();
      riter != RowsToRemove.rend(); ++riter) {
//    LOG(1, "Removing group item at row " << *riter);
    removeRows(*riter, 1, invisibleRootItem()->index());
  }
  // and done
//  LOG(1, "Done with update.");
}