/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2015 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 .
 */
/*
 * QtSelectionChangedAgent.cpp
 *
 *  Created on: Jul 17, 2015
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "QtSelectionChangedAgent.hpp"
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Observer/Notification.hpp"
#include 
#include 
#include "Atom/atom.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "molecule.hpp"
#include "World.hpp"
QtSelectionChangedAgent::QtSelectionChangedAgent(QObject *_parent) :
  QObject(_parent),
  Observable("QtSelectionChangedAgent")
{
  World::getInstance().signOn(this, World::SelectionChanged);
}
QtSelectionChangedAgent::~QtSelectionChangedAgent()
{
  World::getInstance().signOff(this, World::SelectionChanged);
}
void QtSelectionChangedAgent::update(Observable *publisher)
{
  ASSERT(0,
      "QtSelectionChangedAgent::update() - we are not signed on for general updates.");
}
template 
std::pair, typename std::vector > getSelectedUnselectedDifference(
    typename std::vector &_oldselecteditems,
    typename std::vector &_newselecteditems)
{
  std::pair, typename std::vector > SelectedUnselected;
  std::sort(_oldselecteditems.begin(), _oldselecteditems.end());
  std::sort(_newselecteditems.begin(), _newselecteditems.end());
  // first but not in second: all newly selected
  std::set_difference(
      _newselecteditems.begin(), _newselecteditems.end(),
      _oldselecteditems.begin(), _oldselecteditems.end(),
      std::back_inserter(SelectedUnselected.first));
  // first but not in second: all before selected but now no longer
  std::set_difference(
      _oldselecteditems.begin(), _oldselecteditems.end(),
      _newselecteditems.begin(), _newselecteditems.end(),
      std::back_inserter(SelectedUnselected.second));
  return SelectedUnselected;
}
void QtSelectionChangedAgent::recieveNotification(Observable *publisher, Notification_ptr notification)
{
  if (dynamic_cast(publisher) != NULL) {
    switch (notification->getChannelNo()) {
      case World::SelectionChanged:
      {
        boost::recursive_mutex::scoped_lock lock(mutex);
        // check what changed and propagated changes specifically
        selectedAtoms_t newlyselectedatoms =
            const_cast(World::getInstance()).getSelectedAtomIds();
        std::pair< selectedAtoms_t, selectedAtoms_t> AtomSelectionDifference =
            getSelectedUnselectedDifference(selectedAtoms, newlyselectedatoms);
        for (selectedAtoms_t::const_iterator selectediter = AtomSelectionDifference.first.begin();
            selectediter != AtomSelectionDifference.first.end(); ++selectediter) {
          const atom * const _atom =
              const_cast(World::getInstance()).getAtom(AtomById(*selectediter));
          if (_atom != NULL) {
            const molecule * const _mol = _atom->getMolecule();
            if (_mol != NULL)
              emit atomSelected(_mol->getId(), *selectediter);
            else
              ELOG(2, "QtSelectionChangedAgent::recieveNotification() - selected atom "
                  << *selectediter << " has no molecule.");
          } else
            ELOG(2, "QtSelectionChangedAgent::recieveNotification() - selected atom "
              << *selectediter << " has disappeared.");
        }
        for (selectedAtoms_t::const_iterator unselectediter = AtomSelectionDifference.second.begin();
            unselectediter != AtomSelectionDifference.second.end(); ++unselectediter) {
          const atom * const _atom =
              const_cast(World::getInstance()).getAtom(AtomById(*unselectediter));
          if (_atom != NULL) {
            const molecule * const _mol = _atom->getMolecule();
            if (_mol != NULL)
              emit atomUnselected(_mol->getId(), *unselectediter);
            else
              ELOG(2, "QtSelectionChangedAgent::recieveNotification() - unselected atom "
                  << *unselectediter << " has no molecule .");
          } else
            ELOG(2, "QtSelectionChangedAgent::recieveNotification() - unselected atom "
                << *unselectediter << " has disappeared.");
        }
        selectedAtoms = newlyselectedatoms;
        selectedMolecules_t newlyselectedmolecules =
            const_cast(World::getInstance()).getSelectedMoleculeIds();
        std::pair< selectedMolecules_t, selectedMolecules_t> MoleculeSelectionDifference =
            getSelectedUnselectedDifference(selectedMolecules, newlyselectedmolecules);
        for (selectedMolecules_t::const_iterator selectediter = MoleculeSelectionDifference.first.begin();
            selectediter != MoleculeSelectionDifference.first.end(); ++selectediter)
          emit moleculeSelected(*selectediter);
        for (selectedMolecules_t::const_iterator unselectediter = MoleculeSelectionDifference.second.begin();
            unselectediter != MoleculeSelectionDifference.second.end(); ++unselectediter)
          emit moleculeUnselected(*unselectediter);
        selectedMolecules = newlyselectedmolecules;
        break;
      }
      default:
        ASSERT(0, "QtSelectionChangedAgent::recieveNotification() - received notification from unexpected channel.");
    }
  }
}
void QtSelectionChangedAgent::subjectKilled(Observable *publisher)
{
  // World is killed last anyway
}