/*
 * 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 "Views/Qt4/QtMoleculeList.hpp"
#include 
#include 
#include "CodePatterns/MemDebug.hpp"
#include "Atom/atom.hpp"
#include "Formula.hpp"
#include "molecule.hpp"
#include "MoleculeListClass.hpp"
#include "Actions/SelectionAction/Molecules/MoleculeByIdAction.hpp"
#include "Actions/SelectionAction/Molecules/NotMoleculeByIdAction.hpp"
using namespace std;
// maybe this should go with the definition of molecules
// some attributes need to be easier to find for molecules
// these attributes are skipped so far
const int QtMoleculeList::COLUMNCOUNT = COLUMNTYPES_MAX;
const char *QtMoleculeList::COLUMNNAMES[QtMoleculeList::COLUMNCOUNT]={"Name","Visibility", "Atoms","Formula","Occurrence"/*,"Size"*/};
QtMoleculeList::QtMoleculeList(QWidget * _parent) :
  QTreeWidget (_parent),
  Observer("QtMoleculeList")
{
  setColumnCount(COLUMNCOUNT);
  setSelectionMode(QAbstractItemView::MultiSelection);
  QStringList header;
  for(int i=0; i("QItemSelection");
  //connect(this,SIGNAL(cellChanged(int,int)),this,SLOT(moleculeChanged(int,int)));
  connect(selectionModel(),SIGNAL(selectionChanged(QItemSelection, QItemSelection)),this,SLOT(rowsSelected(QItemSelection, QItemSelection)));
  connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(visibilityChanged(QTreeWidgetItem*, int)));
}
QtMoleculeList::~QtMoleculeList()
{
  World::getInstance().signOff(this, World::MoleculeInserted);
  World::getInstance().signOff(this, World::MoleculeRemoved);
}
void QtMoleculeList::update(Observable *publisher) {
  ASSERT(0,
      "QtMoleculeList::update() - we did not sign up for any global updates.");
}
void QtMoleculeList::recieveNotification(Observable *publisher, Notification_ptr notification) {
  if (selecting)
    return;
  dirty = true;
}
void QtMoleculeList::refill() {
  clearing = true;
  const std::vector &molecules = World::getInstance().getAllMolecules();
  clear();
  formula.clear();
  FormulaVisibilityCountMap.clear();
  for (std::vector::const_iterator iter = molecules.begin();
      iter != molecules.end();
      iter++) {
    // find group if already in list
    QTreeWidgetItem *groupItem = NULL;
    const std::string &molecule_formula = (*iter)->getFormula().toString();
    FormulaTreeItemMap_t::const_iterator formulaiter =
        formula.find(molecule_formula);
    // new molecule type -> create new group
    if (formulaiter == formula.end()){
      // 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
      groupItem = new QTreeWidgetItem(this);
      formula.insert( std::make_pair(molecule_formula, groupItem) );
      // fill item
      groupItem->setText(NAME, QString("default"));
      groupItem->setFlags((groupItem->flags() | Qt::ItemIsUserCheckable) ^ Qt::ItemIsSelectable);
      groupItem->setCheckState(VISIBILITY, Qt::Unchecked);
      groupItem->setText(ATOMCOUNT, QString::number(0));
      groupItem->setText(FORMULA, QString(""));
      groupItem->setText(OCCURRENCE, "0");
      groupItem->setData(0, Qt::UserRole, QVariant(-1));
    } else {
      groupItem = formulaiter->second;
    }
    // add molecule
    QTreeWidgetItem *molItem = new QTreeWidgetItem(groupItem);
    molItem->setText(NAME, QString((*iter)->getName().c_str()));
    molItem->setFlags(molItem->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
    molItem->setCheckState(VISIBILITY, Qt::Unchecked);
    molItem->setText(ATOMCOUNT, QString::number((*iter)->getAtomCount()));
    molItem->setText(FORMULA, QString(molecule_formula.c_str()));
    const int index = (*iter)->getId();
    molItem->setData(0, Qt::UserRole, QVariant(index));
    molItem->setSelected(World::getInstance().isSelected(*iter));
    // increase group occurrence
    int count = groupItem->text(OCCURRENCE).toInt() + 1;
    groupItem->setText(OCCURRENCE, QString::number(count));
  }
  dirty = false;
  clearing = false;
}
void QtMoleculeList::paintEvent(QPaintEvent * event)
{
  if (dirty)
    refill();
  QTreeWidget::paintEvent(event);
}
void QtMoleculeList::subjectKilled(Observable *publisher) {
}
void QtMoleculeList::visibilityChanged(QTreeWidgetItem* item, int column)
{
  if ((!clearing) && (!ChangingChildrensVisibility))
    if (column == VISIBILITY) {
      const moleculeId_t molid = item->data(0, Qt::UserRole).toInt();
      const bool visible = item->checkState(VISIBILITY);
      if (molid != (unsigned int)-1) { // molecule item
        const molecule * const _molecule =
            World::getInstance().getMolecule(MoleculeById(molid));
        ASSERT( _molecule != NULL,
            "QtMoleculeList::visibilityChanged() - molecule with id "
            +toString(molid)+" is not known to World.");
        const std::string &molecule_formula = _molecule->getFormula().toString();
        ASSERT( FormulaVisibilityCountMap.count(molecule_formula) != 0,
            "QtMoleculeList::visibilityChanged() - molecule with formula " +molecule_formula
            +" is not present in FormulaVisibilityCountMap.");
        // get parent
        QTreeWidgetItem *groupItem = item->parent();
        ASSERT( groupItem != NULL,
            "QtMoleculeList::visibilityChanged() - item with id "+toString(molid)
            +" 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->text(OCCURRENCE).toInt()))
            groupItem->setCheckState(VISIBILITY, Qt::Checked);
        } else {
          --(FormulaVisibilityCountMap[molecule_formula]);
          // none selected anymore?
          if (FormulaVisibilityCountMap[molecule_formula] == 0)
            groupItem->setCheckState(VISIBILITY, Qt::Unchecked);
        }
        ChangingChildrensVisibility = false;
        emit moleculesVisibilityChanged(molid, visible);
      } else { // group item
        // go through all children, but don't enter for groupItem once more
        ChangingChildrensVisibility = true;
        for (int i=0;ichildCount();++i) {
          QTreeWidgetItem *molItem = item->child(i);
          const moleculeId_t molid = molItem->data(0, Qt::UserRole).toInt();
          ASSERT( molid != (unsigned int)-1,
              "QtMoleculeList::visibilityChanged() - to child with index"
              +toString(i)+" there is no molecule?");
          molItem->setCheckState(VISIBILITY, visible ? Qt::Checked : Qt::Unchecked);
          // emit signal
          emit moleculesVisibilityChanged(molid, visible);
        }
        // set current number of visible children
        const std::string molecule_formula =
            item->text(FORMULA).toStdString();
        FormulaVisibilityCountMap[molecule_formula] =
            visible ? item->text(OCCURRENCE).toInt() : 0;
        ChangingChildrensVisibility = false;
      }
    }
}
void QtMoleculeList::moleculeChanged() {
  /*int idx = verticalHeaderItem(row)->data(Qt::UserRole).toInt();
  molecule *mol = molecules->ReturnIndex(idx);
  string cellValue = item(row,NAME)->text().toStdString();
  if(mol->getName() != cellValue && cellValue !="") {
    mol->setName(cellValue);
  }
  else if(cellValue==""){
    item(row,NAME)->setText(QString(mol->getName().c_str()));
  }*/
}
void QtMoleculeList::rowsSelected(const QItemSelection & selected, const QItemSelection & deselected){
  if (clearing)
    return;
  if (selecting)
    return;
  selecting = true;
  // Select all molecules which belong to newly selected rows.
  QModelIndex index;
  QModelIndexList items = selected.indexes();
  foreach (index, items)
    if (index.column() == 0){
      int mol_id = model()->data(index, Qt::UserRole).toInt();
      if (mol_id < 0)
        continue;
      //std::cout << "select molecule" << std::endl;
      MoleCuilder::SelectionMoleculeById(mol_id);
    }
  // Unselect all molecules which belong to newly unselected rows.
  items = deselected.indexes();
  foreach (index, items)
    if (index.column() == 0){
      int mol_id = model()->data(index, Qt::UserRole).toInt();
      if (mol_id < 0)
        continue;
      //std::cout << "unselect molecule" << std::endl;
      MoleCuilder::SelectionNotMoleculeById(mol_id);
    }
  selecting = false;
}