/*
 * 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 .
 */
/** \file FormatParserStorage.cpp
 *
 *  date: Jun, 22 2010
 *  author: heber
 *
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include 
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "molecule.hpp"
#include "FormatParserStorage.hpp"
#include "ParserTypes.hpp"
#include "MpqcParser.hpp"
#include "PcpParser.hpp"
#include "PdbParser.hpp"
#include "Psi3Parser.hpp"
#include "TremoloParser.hpp"
#include "XmlParser.hpp"
#include "XyzParser.hpp"
#include "CodePatterns/Singleton_impl.hpp"
const std::string FormatParserStorage::unknownTypeString("unknown");
/** Constructor of class FormatParserStorage.
 */
FormatParserStorage::FormatParserStorage()
{
  ParserList.resize(ParserTypes_end, NULL);
  ParserStream.resize(ParserTypes_end, NULL);
  ParserPresent.resize(ParserTypes_end, false);
  ParserDesiredOutputFormat.resize(ParserTypes_end, false);
#include "ParserTypes.def"
#define insert_print(z,n,seq,map, before, after) \
   map .insert( std::make_pair(  \
     BOOST_PP_SEQ_ELEM(n, seq) \
     , before < \
       BOOST_PP_SEQ_ELEM(n, seq) \
       > after \
     ) );
  
#define insert_invert_print(z,n,seq,map, before, after) \
   map .insert( std::make_pair( before < \
   BOOST_PP_SEQ_ELEM(n, seq) \
   > after, \
   BOOST_PP_SEQ_ELEM(n, seq) \
  ) );
  // fill ParserNames
#if defined ParserTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) insert_print(~, n, PARSERSEQUENCE, ParserNames, FormatParserTrait, ::name)
#define BOOST_PP_LOCAL_LIMITS  (0, ParserTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  // fill ParserLookupNames
#if defined ParserTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) insert_invert_print(~, n, PARSERSEQUENCE, ParserLookupNames, FormatParserTrait, ::name)
#define BOOST_PP_LOCAL_LIMITS  (0, ParserTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  // fill ParserSuffixes
#if defined ParserTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) insert_print(~, n, PARSERSEQUENCE, ParserSuffixes, FormatParserTrait, ::suffix)
#define BOOST_PP_LOCAL_LIMITS  (0, ParserTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  // fill ParserLookupSuffixes
#if defined ParserTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) insert_invert_print(~, n, PARSERSEQUENCE, ParserLookupSuffixes, FormatParserTrait, ::suffix)
#define BOOST_PP_LOCAL_LIMITS  (0, ParserTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  // fill ParserAddFunction
#if defined ParserTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) insert_print(~, n, PARSERSEQUENCE, ParserAddFunction, &FormatParserStorage::addParser, )
#define BOOST_PP_LOCAL_LIMITS  (0, ParserTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
#undef insert_print
#undef insert_invert_print
#include "ParserTypes.undef"
  //std::cout << "ParserNames:" << std::endl << ParserNames << std::endl;
  //std::cout << "ParserSuffixes:" << std::endl << ParserSuffixes << std::endl;
  //std::cout << "ParserLookupNames:" << std::endl << ParserLookupNames << std::endl;
  //std::cout << "ParserLookupSuffixes:" << std::endl << ParserLookupSuffixes << std::endl;
  //std::cout << "ParserAddFunction:" << std::endl << ParserAddFunction << std::endl;
}
/** Destructor of class FormatParserStorage.
 * Free all stored FormatParsers. 
 * Save on Exit.
 */
FormatParserStorage::~FormatParserStorage()
{
  for (ParserTypes iter = ParserTypes_begin; iter < ParserTypes_end; ++iter)
    if (ParserPresent[iter]) {
      // this will also flush the ParserStream
      delete ParserList[iter];
      // then we remove the stream itself
      if (ParserStream[iter] != NULL) {
        if (ParserStream[iter]->is_open())
          ParserStream[iter]->close();
        delete ParserStream[iter];
      }
    }
}
/** Tests whether a file and parsers are selected for saving.
 */
bool FormatParserStorage::isAbleToSave()
{
  if (prefix.empty())
    return false;
  for (ParserTypes iter = ParserTypes_begin; iter < ParserTypes_end; ++iter)
    if (ParserPresent[iter] && ParserDesiredOutputFormat[iter])
      return true;
  return false;
}
/** Sets the filename of all current parsers in storage to prefix.suffix.
 * \param &prefix prefix to use.
 */
void FormatParserStorage::SetOutputPrefixForAll(std::string &_prefix)
{
  prefix=_prefix;
};
/** Sets \a type as a format to be stored on call of SaveAll.
 *
 * @param type type to add to desired output formats
 */
void FormatParserStorage::setOutputFormat(ParserTypes type)
{
  LOG(0, "STATUS: Adding " << ParserNames[type] << " type to output.");
  ParserDesiredOutputFormat[type] = true;
}
/** Sets \a type as a format to be stored on call of SaveAll.
 *
 * @param type type to add to desired output formats
 */
void FormatParserStorage::setOutputFormat(std::string type)
{
  std::map::const_iterator iter = ParserLookupNames.find(type);
  ASSERT(iter != ParserLookupNames.end(),
      "FormatParserStorage::setOutputFormat() - output format "+type+" is unknown.");
  setOutputFormat(iter->second);
}
/** Saves the world in the desired output formats.
 *
 */
void FormatParserStorage::SaveAll()
{
  std::string filename;
  for (ParserTypes iter = ParserTypes_begin; iter < ParserTypes_end; ++iter)
    if (ParserPresent[iter] && ParserDesiredOutputFormat[iter]) {
      filename = prefix;
      filename += ".";
      filename += ParserSuffixes[iter];
      ParserStream[iter] = new std::ofstream(filename.c_str());
      ParserList[iter]->setOstream((std::ostream *)ParserStream[iter]);
    }
}
ParserTypes FormatParserStorage::getTypeFromName(std::string type)
{
  if (ParserLookupNames.find(type) == ParserLookupNames.end()) {
    ELOG(1, "Unknown type " << type << ".");
    return ParserTypes_end;
  } else
    return ParserLookupNames[type];
}
ParserTypes FormatParserStorage::getTypeFromSuffix(std::string type)
{
  if (ParserLookupSuffixes.find(type) == ParserLookupSuffixes.end()) {
    ELOG(1, "Unknown type " << type << ".");
    return ParserTypes_end;
  } else
    return ParserLookupSuffixes[type];
}
const std::string &FormatParserStorage::getNameFromType(ParserTypes type)
{
  if (ParserNames.find(type) == ParserNames.end()) {
    ELOG(1, "Unknown type " << type << ".");
    return unknownTypeString;
  } else
    return ParserNames[type];
}
const std::string &FormatParserStorage::getSuffixFromType(ParserTypes type)
{
  if (ParserSuffixes.find(type) == ParserSuffixes.end()) {
    ELOG(1, "Unknown type " << type << ".");
    return unknownTypeString;
  } else
    return ParserSuffixes[type];
}
bool FormatParserStorage::add(ParserTypes ptype)
{
  if (ptype != ParserTypes_end) {
    if (ParserAddFunction.find(ptype) != ParserAddFunction.end()) {
      (getInstance().*(ParserAddFunction[ptype]))(); // we still need an object to work on ...
      return true;
    } else {
      ELOG(1, "No parser to add for this known type " << ParserNames[ptype] << ", not implemented?");
      return false;
    }
  } else {
    return false;
  }
}
bool FormatParserStorage::add(std::string type)
{
  enum ParserTypes Ptype = getTypeFromName(type);
  return add(Ptype);
}
/** Recognizes type of file and parse via FormatParserStorage::load().
 * \param filename path and filename
 * \return true - parsing ok, false - suffix unknown
 */
bool FormatParserStorage::load(boost::filesystem::path filename)
{
  return load(filename.string());
}
/** Recognizes type of file and parse via FormatParserStorage::load().
 * \param filename path and filename
 * \return true - parsing ok, false - suffix unknown
 */
bool FormatParserStorage::load(std::string &filename)
{
  std::string FilenameSuffix = filename.substr(filename.find_last_of('.')+1, filename.length());
  ifstream input;
  LOG(0, "STATUS: Loading filler molecule " << filename
      << " of suffix " << FilenameSuffix << ".");
  input.open(filename.c_str());
  const bool status = load(input, FilenameSuffix);
  input.close();
  return status;
}
/** Parses an istream depending on its suffix
 * \param &input input stream
 * \param suffix
 * \return true - parsing ok, false - suffix unknown
 */
bool FormatParserStorage::load(std::istream &input, std::string &suffix)
{
  enum ParserTypes type = getTypeFromSuffix(suffix);
  if (type != ParserTypes_end)
    get(type).load(&input);
  else
    return false;
  return true;
}
/** Stores all selected atoms in an ostream depending on its suffix
 * \param &output output stream
 * \param suffix
 * \return true - storing ok, false - suffix unknown
 */
bool FormatParserStorage::saveSelectedAtoms(std::ostream &output, std::string suffix)
{
  const std::vector atoms =
      const_cast(World::getInstance()).getSelectedAtoms();
  return save(output, suffix, atoms);
}
/** Stores all selected atoms in an ostream depending on its suffix
 * We store in the order of the atomic ids, not in the order they appear in the molecules.
 * Hence, we first create a vector from all selected molecules' atoms.
 *
 * TODO: Change here atom * to const atom * when FormatParserStorage::save() uses vector
 *
 * \param &output output stream
 * \param suffix
 * \return true - storing ok, false - suffix unknown
 */
bool FormatParserStorage::saveSelectedMolecules(std::ostream &output, std::string suffix)
{
  const std::vector molecules =
      const_cast(World::getInstance()).getSelectedMolecules();
  std::map IdAtoms;
  for (std::vector::const_iterator MolIter = molecules.begin();
      MolIter != molecules.end();
      ++MolIter) {
    for(molecule::const_iterator AtomIter = (*MolIter)->begin();
        AtomIter != (*MolIter)->end();
        ++AtomIter) {
      IdAtoms.insert( make_pair((*AtomIter)->getId(), (*AtomIter)) );
    }
  }
  std::vector atoms;
  atoms.reserve(IdAtoms.size());
  for (std::map::const_iterator iter = IdAtoms.begin();
      iter != IdAtoms.end();
      ++iter) {
    atoms.push_back(iter->second);
  }
  return save(output, suffix, atoms);
}
/** Stores world in an ostream depending on its suffix
 * \param &output output stream
 * \param suffix
 * \return true - storing ok, false - suffix unknown
 */
bool FormatParserStorage::saveWorld(std::ostream &output, std::string suffix)
{
  const std::vector atoms =
      const_cast(World::getInstance()).getAllAtoms();
  return save(output, suffix, atoms);
}
/** Stores a given vector of \a atoms in an ostream depending on its suffix
 * \param &output output stream
 * \param suffix
 * \return true - storing ok, false - suffix unknown
 */
bool FormatParserStorage::save(
    std::ostream &output,
    std::string suffix,
    const std::vector &atoms)
{
  enum ParserTypes type = getTypeFromSuffix(suffix);
  if (type != ParserTypes_end)
    get(type).save(&output, atoms);
  else
    return false;
  return true;
}
/** Returns reference to the desired output parser as FormatParser, adds if not present.
 * \param _type type of desired parser
 * \return reference to the output FormatParser with desired type
 */
FormatParserInterface &FormatParserStorage::get(ParserTypes _type)
{
  if (!ParserPresent[_type]) {
    add(_type);
  }
  return *ParserList[_type];
}
CONSTRUCT_SINGLETON(FormatParserStorage)