/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2017 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 .
 */
/*
 * BoostGraphCreator.cpp
 *
 *  Created on: May 17, 2017
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
//#include "CodePatterns/MemDebug.hpp"
#include "BoostGraphCreator.hpp"
#include 
#include 
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "molecule.hpp"
void BoostGraphCreator::createFromMolecule(
    const molecule &_mol,
    const predicate_t &_pred)
{
  createFromRange(_mol.begin(), _mol.end(), _mol.getAtomCount(), _pred);
}
static bool predicateAnd(
    const BoostGraphCreator::predicate_t &_pred1,
    const BoostGraphCreator::predicate_t &_pred2,
    const bond &_bond)
{ return (_pred1(_bond) && _pred2(_bond)); }
static atomId_t getAtomId(
    const atom *_atom
    )
{ return _atom->getId(); }
static bool inSetPredicate(
  const std::vector &_atomids,
  const bond &_bond)
{
  const atomId_t leftid = _bond.leftatom->getId();
  const atomId_t rightid = _bond.rightatom->getId();
  return std::binary_search(_atomids.begin(), _atomids.end(), leftid)
      && std::binary_search(_atomids.begin(), _atomids.end(), rightid);
}
void BoostGraphCreator::createFromAtoms(
    const std::vector &_atoms,
    const predicate_t &_pred)
{
  // sort makes binary_search possible
  std::vector atomids;
  std::transform(_atoms.begin(), _atoms.end(),
      std::back_inserter(atomids), getAtomId);
  ASSERT( _atoms.size() == atomids.size(),
      "BoostGraphCreator::createFromAtom() - atomids "
      +toString(atomids.size())+" and atoms "+toString(_atoms.size())
      +" differ in size?");
  std::sort(atomids.begin(), atomids.end());
  const predicate_t predicate = boost::bind(inSetPredicate, boost::ref(atomids), _1);
  createFromRange::const_iterator>(
      const_cast &>(_atoms).begin(),
      const_cast &>(_atoms).end(),
      _atoms.size(),
      boost::bind(predicateAnd, _pred, predicate, _1));
}
BoostGraphCreator::nodeId_t BoostGraphCreator::getNodeId(
    const atomId_t &_atomid) const
{
  atomids_nodeids_t::const_iterator iter =
      atomids_nodeids.find(_atomid);
  return (iter == atomids_nodeids.end()) ? (nodeId_t)-1 : iter->second;
}
template 
void BoostGraphCreator::createFromRange(
    const iterator &_begin,
    const iterator &_end,
    const size_t &_no_nodes,
    const predicate_t &_pred
    )
{
  graph = UndirectedGraph();
  // add vertices
  for(iterator iter = _begin; iter != _end; ++iter) {
    const atomId_t atomid = (*iter)->getId();
    Vertex v = boost::add_vertex(atomid, graph);
    const atomId_t vertexname = boost::get(boost::get(boost::vertex_name, graph), v);
    const nodeId_t vertexindex = boost::get(boost::get(boost::vertex_index, graph), v);
    LOG(2, "DEBUG: Adding node " << vertexindex << " associated to atom #" << vertexname);
    ASSERT( vertexname == atomid,
        "BoostGraphCreator::createFromRange() - atomid "+toString(atomid)
        +" is not name of vertex "+toString(vertexname)+".");
    atomids_nodeids.insert( std::make_pair(vertexname, vertexindex) );
  }
  // add edges
  for(iterator iter = _begin; iter != _end; ++iter) {
    LOG(2, "DEBUG: Looking at atom " << (*iter)->getId());
    const BondList& ListOfBonds = (*iter)->getListOfBonds();
    for(BondList::const_iterator bonditer = ListOfBonds.begin();
        bonditer != ListOfBonds.end(); ++bonditer) {
      LOG(2, "DEBUG: Looking at bond " << *(*bonditer));
      const atomId_t leftid = (*bonditer)->leftatom->getId();
      const nodeId_t leftnodeid = getNodeId(leftid);
      const atomId_t rightid = (*bonditer)->rightatom->getId();
      const nodeId_t rightnodeid = getNodeId(rightid);
      // only pick each bond once and evaluate predicate
      if ((leftid == (*iter)->getId())
          && (_pred(**bonditer))) {
        LOG(3, "DEBUG: ADDING edge " << leftnodeid << " <-> " << rightnodeid);
        boost::add_edge(leftnodeid, rightnodeid, graph);
      } else {
        LOG(3, "DEBUG: Discarding edge " << leftnodeid << " <-> " << rightnodeid);
      }
    }
  }
  LOG(2, "DEBUG: We have " << getNumVertices() << " nodes and " << getNumEdges()
      << " edges in the molecule graph.");
}