/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2016 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 .
 */
/*
 * AtomFragmentsMap.cpp
 *
 *  Created on: Mar 7, 2016
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Singleton_impl.hpp"
#include "AtomFragmentsMap.hpp"
#include "AtomIdSet.hpp"
#include "Fragmentation/Graph.hpp"
#include "Fragmentation/KeySet.hpp"
void AtomFragmentsMap::insert(
    const Graph &_graph)
{
  /// create a map of atom to keyset (below equal MaxOrder)
  LOG(1, "INFO: Placing all atoms and their keysets into internal map.");
  for (Graph::const_iterator keysetiter = _graph.begin();
      keysetiter != _graph.end(); ++keysetiter) {
    const KeySet &keyset = keysetiter->first;
    LOG(2, "DEBUG: Current keyset is " << keyset);
    if (keyset.empty())
      continue;
    for (KeySet::const_iterator keyiter = keyset.begin();
        keyiter != keyset.end(); ++keyiter) {
      // either create new list ...
      std::pair inserter =
          atommap.insert( std::make_pair(*keyiter, keysets_t(1, keyset) ));
      // ... or push to end
      if (inserter.second) {
        LOG(3, "DEBUG: Created new entry in map.");
      } else {
        LOG(3, "DEBUG: Added keyset to present entry.");
        inserter.first->second.push_back(keyset);
      }
    }
    // add empty entry to fullkeysets, too (if already present, we ignore that)
    fullkeysets.insert( std::make_pair(keyset, indices_t()) );
  }
  LOG(2, "DEBUG: There are now " << atommap.size() << " entries in lookup.");
}
AtomFragmentsMap::AtomFragmentsMap_t AtomFragmentsMap::getMap(
    const std::vector &_candidates,
    size_t _MaxOrder) const
{
  typedef std::vector candidates_t;
  AtomFragmentsMap_t fragmentmap;
  for (candidates_t::const_iterator candidateiter = _candidates.begin();
      candidateiter != _candidates.end(); ++candidateiter) {
    const atomId_t atomid = *candidateiter;
    const AtomFragmentsMap_t::const_iterator iter = atommap.find(atomid);
    ASSERT( iter != atommap.end(),
        "AtomFragmentsMap::getMap() - could not find atom "
        +toString(atomid)+" in lookup.");
    // due to MaxOrder we need to copy selectively and hence look at each KeySet in turn
    const keysets_t &keysets = iter->second;
    for (keysets_t::const_iterator keyiter = keysets.begin();
        keyiter != keysets.end(); ++keyiter) {
      const KeySet &keyset = *keyiter;
      if ((keyset.size() > _MaxOrder) || (keyset.empty()))
        continue;
      std::pair inserter =
          fragmentmap.insert( std::make_pair(atomid, keysets_t(1, keyset) ));
      // ... or push to end
      if (inserter.second) {
        LOG(3, "DEBUG: Created new entry in lookup map.");
      } else {
        LOG(3, "DEBUG: Added keyset to present entry in lookup map.");
        inserter.first->second.push_back(keyset);
      }
    }
  }
  LOG(4, "DEBUG: Copied part of lookup map contains " << fragmentmap.size() << " keys.");
  return fragmentmap;
}
bool AtomFragmentsMap::addFullKeyset(const KeySet &_keyset, const indices_t &_fullkeyset)
{
  FragmentFullKeysetMap_t::iterator iter = fullkeysets.find(_keyset);
  if ((iter == fullkeysets.end()) || (!iter->second.empty()))
    return false;
  else {
    iter->second = _fullkeyset;
  }
  return true;
}
const AtomFragmentsMap::indices_t &
AtomFragmentsMap::getFullKeyset(const KeySet &_keyset) const
{
  static indices_t emptyset;
  FragmentFullKeysetMap_t::const_iterator iter = fullkeysets.find(_keyset);
  if (iter == fullkeysets.end())
    return emptyset;
  else
    return iter->second;
}
bool AtomFragmentsMap::checkCompleteness() const
{
  bool status = true;
  for (FragmentFullKeysetMap_t::const_iterator iter = fullkeysets.begin();
      iter != fullkeysets.end(); ++iter)
    status &= (iter->second.size() != 0);
  return status;
}
CONSTRUCT_SINGLETON(AtomFragmentsMap)
// we need to explicitly instantiate the serialization functions
BOOST_CLASS_EXPORT_IMPLEMENT(AtomFragmentsMap)