/*
 * 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 .
 */
/*
 * Graph.cpp
 *
 *  Created on: Oct 20, 2011
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include 
#include 
#include "Graph.hpp"
#include "CodePatterns/Log.hpp"
#include "Atom/atom.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "Fragmentation/AdaptivityMap.hpp"
#include "Helpers/defs.hpp"
#include "Helpers/helpers.hpp"
#include "World.hpp"
/** Constructor for class Graph.
 *
 */
Graph::Graph()
{}
/** Destructor for class Graph.
 *
 */
Graph::~Graph()
{}
/** Inserts each KeySet in \a graph into \a this.
 * \param graph1 graph whose KeySet are inserted into \this graph
 * \param *counter keyset counter that gets increased
 */
void Graph::InsertGraph(const Graph &graph, int &counter)
{
  GraphTestPair testGraphInsert;
  for(Graph::const_iterator runner = graph.begin(); runner != graph.end(); runner++) {
    testGraphInsert = insert(GraphPair ((*runner).first,pair(++counter,((*runner).second).second)));  // store fragment number and current factor
    if (testGraphInsert.second) {
      LOG(2, "INFO: KeySet " << counter-1 << " successfully inserted.");
    } else {
      LOG(2, "INFO: KeySet " << counter-1 << " failed to insert, present fragment is " << ((*(testGraphInsert.first)).second).first);
      ((*(testGraphInsert.first)).second).second += (*runner).second.second;
      LOG(2, "INFO: New factor is " << (*(testGraphInsert.first)).second.second << ".");
    }
  }
};
/** Parses the KeySet file and fills \a this from the known molecule structure.
 * Does two-pass scanning:
 * -# Scans the keyset file and initialises a temporary graph
 * -# Scans TEFactors file and sets the TEFactor of each key set in the temporary graph accordingly
 * Finally, the temporary graph is inserted into the given \a FragmentList for return.
 * \param &path path to file
 * \return true - parsing successfully, false - failure on parsing (FragmentList will be NULL)
 */
bool Graph::ParseKeySetFile(const std::string &path)
{
  bool status = true;
  std::ifstream InputFile;
  std::stringstream line;
  GraphTestPair testGraphInsert;
  int NumberOfFragments = 0;
  std::string filename;
  // 1st pass: open file and read
  LOG(1, "INFO: Parsing the KeySet file ... ");
  filename = path + KEYSETFILE;
  InputFile.open(filename.c_str());
  if (InputFile.good()) {
    // each line represents a new fragment
    char buffer[MAXSTRINGSIZE];
    // 1. parse keysets and insert into temp. graph
    while (!InputFile.eof()) {
      InputFile.getline(buffer, MAXSTRINGSIZE);
      KeySet CurrentSet;
      if ((strlen(buffer) > 0) && (CurrentSet.ScanBufferIntoKeySet(buffer))) {  // if at least one valid atom was added, write config
        testGraphInsert = insert(GraphPair (CurrentSet,pair(NumberOfFragments++,1)));  // store fragment number and current factor
        if (!testGraphInsert.second) {
          ELOG(0, "KeySet file must be corrupt as there are two equal key sets therein!");
          performCriticalExit();
        }
      }
    }
    // 2. Free and done
    InputFile.close();
    InputFile.clear();
    LOG(1, "INFO: ... done.");
  } else {
    ELOG(1, "File " << filename << " not found.");
    status = false;
  }
  return status;
};
/** Stores key sets to file.
 * \param &path path to file
 * \return true - file written successfully, false - writing failed
 */
bool Graph::StoreKeySetFile(const std::string &path) const
{
  bool status =  true;
  std::string line = path + KEYSETFILE;
  std::ofstream output(line.c_str());
  // open KeySet file
  LOG(1, "INFO: Saving key sets of the total graph ... ");
  if(output.good()) {
    for(Graph::const_iterator runner = begin(); runner != end(); runner++) {
      for (KeySet::const_iterator sprinter = (*runner).first.begin();sprinter != (*runner).first.end(); sprinter++) {
        if (sprinter != (*runner).first.begin())
          output << "\t";
        output << *sprinter;
      }
      output << std::endl;
    }
    LOG(1, "INFO: done.");
  } else {
    ELOG(0, "Unable to open " << line << " for writing keysets!");
    performCriticalExit();
    status = false;
  }
  output.close();
  return status;
};
/** Parses the TE factors file and fills \a *FragmentList from the known molecule structure.
 * -# Scans TEFactors file and sets the TEFactor of each key set in the temporary graph accordingly
 * \param *path path to file
 * \return true - parsing successfully, false - failure on parsing
 */
bool Graph::ParseTEFactorsFile(char *path)
{
  bool status = true;
  std::ifstream InputFile;
  std::stringstream line;
  GraphTestPair testGraphInsert;
  int NumberOfFragments = 0;
  double TEFactor;
  char filename[MAXSTRINGSIZE];
  // 2nd pass: open TEFactors file and read
  LOG(1, "INFO: Parsing the TEFactors file ... ");
  sprintf(filename, "%s/%s%s", path, FRAGMENTPREFIX, TEFACTORSFILE);
  InputFile.open(filename);
  if (InputFile != NULL) {
    // 3. add found TEFactors to each keyset
    NumberOfFragments = 0;
    for(Graph::iterator runner = begin();runner != end(); runner++) {
      if (!InputFile.eof()) {
        InputFile >> TEFactor;
        (*runner).second.second = TEFactor;
        LOG(2, "INFO: Setting " << ++NumberOfFragments << " fragment's TEFactor to " << (*runner).second.second << ".");
      } else {
        status = false;
        break;
      }
    }
    // 4. Free and done
    InputFile.close();
    LOG(1, "INFO: done.");
  } else {
    LOG(1, "INFO: File " << filename << " not found.");
    status = false;
  }
  return status;
};
/** Stores TEFactors to file.
 * \param *out output stream for debugging
 * \param KeySetList Graph with factors
 * \param *path path to file
 * \return true - file written successfully, false - writing failed
 */
bool Graph::StoreTEFactorsFile(char *path) const
{
  ofstream output;
  bool status =  true;
  string line;
  // open TEFactors file
  line = path;
  line.append("/");
  line += FRAGMENTPREFIX;
  line += TEFACTORSFILE;
  output.open(line.c_str(), ios::out);
  LOG(1, "INFO: Saving TEFactors of the total graph ... ");
  if(output != NULL) {
    for(Graph::const_iterator runner = begin(); runner != end(); runner++)
      output << (*runner).second.second << endl;
    LOG(1, "INFO: done." << endl);
  } else {
    ELOG(2, "INFO: failed to open " << line << "." << endl);
    status = false;
  }
  output.close();
  return status;
};
/** For a given graph, sorts KeySets into a (index, keyset) map.
 * \return ref to allocated map from index to keyset
 */
AdaptivityMap * Graph::GraphToAdaptivityMap() const
{
  AdaptivityMap *IndexKeySetList = new AdaptivityMap;
  for(const_iterator runner = begin(); runner != end(); runner++) {
    IndexKeySetList->insert( pair(runner->second.first,runner->first) );
  }
  return IndexKeySetList;
};
/** For a given molecule, returns each keyset local to this molecule.
 *
 * @param mol desired molecule
 * @return graph with local keysets (local ids!)
 */
Graph Graph::getLocalGraph(const molecule* mol) const
{
  Graph LocalGraph;
  // go through all key sets, assuming ids are global
  for (const_iterator iter = begin(); iter != end(); ++iter) {
    // check whether each keyset's GLOBAL id is contained in mol_ids (via conversion through World)
    KeySet temp;
    for (KeySet::const_iterator keyiter = iter->first.begin();
        keyiter != iter->first.end(); ++keyiter) {
      const size_t globalid = *keyiter;
      const atom * const Walker = const_cast(World::getInstance()).
          getAtom(AtomById(globalid));
      if (Walker != NULL) {
        if (Walker->getMolecule() != mol) {
          break;
        } else {
          // and store the LOCAL number
          const size_t localid = Walker->getNr();
          temp.insert(localid);
        }
      } else {
        ELOG(0, "Id " << globalid << " is not associated with any atom.");
        break;
      }
    }
    if (temp.size() == iter->first.size()) {
      // if so, add to LocalGraph
      LocalGraph.insert( std::make_pair(temp, iter->second) );
    }
  }
  return LocalGraph;
}