/*
 * 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 .
 */
/*
 * MpqcParser.cpp
 *
 *  Created on: 12.06.2010
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include 
#include 
#include 
#include 
#include 
#include 
#include "CodePatterns/MemDebug.hpp"
#include "MpqcParser.hpp"
#include "MpqcParser_Parameters.hpp"
#include "Atom/atom.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/toString.hpp"
#include "config.hpp"
#include "Element/element.hpp"
#include "Element/periodentafel.hpp"
#include "LinearAlgebra/Vector.hpp"
#include "molecule.hpp"
#include "MoleculeListClass.hpp"
#include "World.hpp"
// declare specialized static variables
const std::string FormatParserTrait::name = "mpqc";
const std::string FormatParserTrait::suffix = "in";
const ParserTypes FormatParserTrait::type = mpqc;
// a converter we often need
ConvertTo FormatParser::Converter;
/** Constructor of MpqcParser.
 *
 */
FormatParser< mpqc >::FormatParser() :
  FormatParser_common(new MpqcParser_Parameters())
{}
/** Destructor of MpqcParser.
 *
 */
FormatParser< mpqc >::~FormatParser()
{}
/** Load an MPQC config file into the World.
 * \param *file input stream
 */
void FormatParser< mpqc >::load(istream *file)
{
  bool MpqcSection = false;
  bool MoleculeSection = false;
  bool GeometrySection = false;
  bool BasisSection = false;
  bool AuxBasisSection = false;
  char line[MAXSTRINGSIZE];
  typedef boost::tokenizer > tokenizer;
  boost::char_separator sep("[]");
  boost::char_separator angularsep("<>");
  boost::char_separator equalitysep(" =");
  boost::char_separator whitesep(" \t");
  ConvertTo toDouble;
  molecule *newmol = World::getInstance().createMolecule();
  newmol->ActiveFlag = true;
  // TODO: Remove the insertion into molecule when saving does not depend on them anymore. Also, remove molecule.hpp include
  World::getInstance().getMolecules()->insert(newmol);
  while (file->good()) {
    file->getline(line, MAXSTRINGSIZE-1);
    std::string linestring(line);
    if ((linestring.find("atoms geometry") == string::npos) && (linestring.find("}") != string::npos)) {
      GeometrySection = false;
    }
    if ((linestring.find(")") != string::npos)) { // ends a section which do not overlap
      MpqcSection = false;
      MoleculeSection = false;
      BasisSection = false;
      AuxBasisSection = false;
    }
    if (MoleculeSection) {
      if (GeometrySection) { // we have an atom
        tokenizer tokens(linestring, sep);
  //      if (tokens.size() != 2)
  //        throw MpqcParseException;
        tokenizer::iterator tok_iter = tokens.begin();
        ASSERT(tok_iter != tokens.end(),
            "FormatParser< mpqc >::load() - missing token for MoleculeSection in line "+linestring+"!");
        std::stringstream whitespacefilter(*tok_iter++);
        std::string element;
        whitespacefilter >> ws >> element;
        ASSERT(tok_iter != tokens.end(),
            "FormatParser< mpqc >::load() - missing token for MoleculeSection in line "+linestring+"!");
        std::string vector = *tok_iter;
        tokenizer vectorcomponents(vector, whitesep);
        Vector X;
  //      if (vectorcomponents.size() != NDIM)
  //        throw MpqcParseException;
        tok_iter = vectorcomponents.begin();
        for (int i=0; isetType(World::getInstance().getPeriode()->FindElement(element));
        newAtom->setPosition(X);
        newmol->AddAtom(newAtom);
        LOG(1, "Adding atom " << *newAtom);
      }
    }
    if (MpqcSection) {
      if (linestring.find("mole<") != string::npos) { // get theory
        tokenizer tokens(linestring, angularsep);
        tokenizer::iterator tok_iter = tokens.begin();
        ++tok_iter;
        ASSERT(tok_iter != tokens.end(),
            "FormatParser< mpqc >::load() - missing token in brackets<> for mole< in line "+linestring+"!");
        std::string value(*tok_iter);
        std::stringstream linestream("theory = "+value);
        linestream >> getParams();
      } else if (linestring.find("integrals<") != string::npos) { // get theory
        tokenizer tokens(linestring, angularsep);
        tokenizer::iterator tok_iter = tokens.begin();
        ++tok_iter;
        ASSERT(tok_iter != tokens.end(),
            "FormatParser< mpqc >::load() - missing token in brackets<> for integrals< in line "+linestring+"!");
        std::string value(*tok_iter);
        std::stringstream linestream("integration = "+value);
        linestream >> getParams();
      } else if ((linestring.find("molecule") == string::npos) && (linestring.find("basis") == string::npos)){
        // molecule and basis must not be parsed in this section
        tokenizer tokens(linestring, equalitysep);
        tokenizer::iterator tok_iter = tokens.begin();
        ASSERT(tok_iter != tokens.end(),
            "FormatParser< mpqc >::load() - missing token before '=' for MpqcSection in line "+linestring+"!");
        std::stringstream whitespacefilter(*tok_iter);
        std::string key;
        whitespacefilter >> ws >> key;
        if (getParams().haveParameter(key)) {
          std::stringstream linestream(linestring);
          linestream >> getParams();
        } else { // unknown keys are simply ignored as long as parser is incomplete
          LOG(2, "INFO: '"+key+"' is unknown and ignored.");
        }
      }
    }
    if (BasisSection) {
      tokenizer tokens(linestring, equalitysep);
      tokenizer::iterator tok_iter = tokens.begin();
      ASSERT(tok_iter != tokens.end(),
          "FormatParser< mpqc >::load() - missing token for BasisSection in line "+linestring+"!");
      std::string key(*tok_iter++);
      ASSERT(tok_iter != tokens.end(),
          "FormatParser< mpqc >::load() - missing value for BasisSection after key "+key+" in line "+linestring+"!");
      std::string value(*tok_iter);
      tok_iter++;
      // TODO: use exception instead of ASSERT
      ASSERT(tok_iter == tokens.end(),
          "FormatParser< mpqc >::load() - more than (key = value) on line "+linestring+".");
      if (key == "name") {
        std::stringstream linestream("basis = "+value);
        linestream >> getParams();
      }
    }
    if (AuxBasisSection) {
      tokenizer tokens(linestring, equalitysep);
      tokenizer::iterator tok_iter = tokens.begin();
      ASSERT(tok_iter != tokens.end(),
          "FormatParser< mpqc >::load() - missing token for AuxBasisSection in line "+linestring+"!");
      std::string key(*tok_iter++);
      ASSERT(tok_iter != tokens.end(),
          "FormatParser< mpqc >::load() - missing value for BasisSection after key "+key+" in line "+linestring+"!");
      std::string value(*tok_iter);
      tok_iter++;
      // TODO: use exception instead of ASSERT
      ASSERT(tok_iter == tokens.end(),
          "FormatParser< mpqc >::load() - more than (key = value) on line "+linestring+".");
      if (key == "name") {
        std::stringstream linestream("aux_basis = "+value);
        linestream >> getParams();
      }
    }
    // set some scan flags
    if (linestring.find("mpqc:") != string::npos) {
      MpqcSection = true;
    }
    if (linestring.find("molecule:") != string::npos) {
      MoleculeSection = true;
    }
    if (linestring.find("atoms geometry") != string::npos) {
      GeometrySection = true;
    }
    if ((linestring.find("basis:") != string::npos) && ((linestring.find("abasis<") == string::npos))) {
      BasisSection = true;
    }
    if (linestring.find("abasis<") != string::npos) {
      AuxBasisSection = true;
    }
  }
  // refresh atom::nr and atom::name
  newmol->getAtomCount();
}
void FormatParser< mpqc >::OutputMPQCLine(ostream * const out, const atom &_atom, const Vector *center) const
{
  Vector recentered(_atom.getPosition());
  recentered -= *center;
  *out << "\t\t" << _atom.getType()->getSymbol() << " [ ";
  {
    std::stringstream posstream;
    posstream << std::setprecision(12) << recentered[0] << "\t" << recentered[1] << "\t" << recentered[2];
    *out << posstream.str();
  }
  *out << " ]" << std::endl;
};
/** Saves all atoms and data into a MPQC config file.
 * \param *file output stream
 * \param atoms atoms to store
 */
void FormatParser< mpqc >::save(ostream *file, const std::vector &atoms)
{
  Vector center;
//  vector allatoms = World::getInstance().getAllAtoms();
  // calculate center
//  for (std::vector::const_iterator runner = atoms.begin();runner != atoms.end(); ++runner)
//    center += (*runner)->getPosition();
//  center.Scale(1./(double)atoms.size());
  center.Zero();
  // first without hessian
  if (file->fail()) {
    ELOG(1, "Cannot open mpqc output file.");
  } else {
    *file << "% Created by MoleCuilder" << endl;
    *file << "mpqc: (" << endl;
    *file << "\tsavestate = " << getParams().getParameter(MpqcParser_Parameters::savestateParam) << endl;
    *file << "\tdo_gradient = " << getParams().getParameter(MpqcParser_Parameters::do_gradientParam) << endl;
    if (Converter(getParams().getParameter(MpqcParser_Parameters::hessianParam))) {
      *file << "\tfreq: (" << endl;
      *file << "\t\tmolecule=$:molecule" << endl;
      *file << "\t)" << endl;
    }
    const std::string theory = getParams().getParameter(MpqcParser_Parameters::theoryParam);
    if (theory == getParams().getTheoryName(MpqcParser_Parameters::CLHF)) {
      *file << "\tmole<" << getParams().getParameter(MpqcParser_Parameters::theoryParam) << ">: (" << endl;
      *file << "\t\tmolecule = $:molecule" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::basisParam) << " = $:basis" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::maxiterParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::maxiterParam)<< endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::memoryParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::memoryParam) << endl;
      *file << "\t)" << endl;
    } else if (theory == getParams().getTheoryName(MpqcParser_Parameters::CLKS)) {
      *file << "\tmole<" << getParams().getParameter(MpqcParser_Parameters::theoryParam) << ">: (" << endl;
      *file << "\t\tfunctional:(name=B3LYP)" << endl;
      *file << "\t\tmolecule = $:molecule" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::basisParam) << " = $:basis" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::maxiterParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::maxiterParam)<< endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::memoryParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::memoryParam) << endl;
      *file << "\t)" << endl;
    } else if (theory == getParams().getTheoryName(MpqcParser_Parameters::MBPT2)) {
      *file << "\tmole<" << getParams().getParameter(MpqcParser_Parameters::theoryParam) << ">: (" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::basisParam) << " = $:basis" << endl;
      *file << "\t\tmolecule = $:molecule" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::memoryParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::memoryParam) << endl;
      *file << "\t\treference: (" << endl;
      *file << "\t\t\t" << getParams().getParameterName(MpqcParser_Parameters::maxiterParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::maxiterParam)<< endl;
      *file << "\t\t\t" << getParams().getParameterName(MpqcParser_Parameters::basisParam) << " = $:basis" << endl;
      *file << "\t\t\tmolecule = $:molecule" << endl;
      *file << "\t\t\t" << getParams().getParameterName(MpqcParser_Parameters::memoryParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::memoryParam) << endl;
      *file << "\t\t)" << endl;
      *file << "\t)" << endl;
    } else if (theory == getParams().getTheoryName(MpqcParser_Parameters::MBPT2_R12)) {
      *file << "\tmole<" << getParams().getParameter(MpqcParser_Parameters::theoryParam) << ">: (" << endl;
      *file << "\t\tmolecule = $:molecule" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::basisParam) << " = $:basis" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::aux_basisParam) << " = $:abasis" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::stdapproxParam)
          << " = \"" << getParams().getParameter(MpqcParser_Parameters::stdapproxParam) << "\"" << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::nfzcParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::nfzcParam) << endl;
      *file << "\t\t" << getParams().getParameterName(MpqcParser_Parameters::memoryParam)
          << " = " << getParams().getParameter(MpqcParser_Parameters::memoryParam) << endl;
      *file << "\t\tintegrals:()" << endl;
      *file << "\t\treference: (" << endl;
      *file << "\t\t\tmolecule = $:molecule" << endl;
      *file << "\t\t\t" << getParams().getParameterName(MpqcParser_Parameters::basisParam) << " = $:basis" << endl;
      *file << "\t\t\tmaxiter = " << getParams().getParameter(MpqcParser_Parameters::maxiterParam) << endl;
      *file << "\t\t\tmemory = " << getParams().getParameter(MpqcParser_Parameters::memoryParam) << endl;
      *file << "\t\t\tintegrals<" << getParams().getParameter(MpqcParser_Parameters::integrationParam) << ">:()" << endl;
      *file << "\t\t)" << endl;
      *file << "\t)" << endl;
    } else {
      ELOG(0, "Unknown level of theory requested for MPQC output file.");
    }
    *file << ")" << endl;
    *file << "molecule: (" << endl;
    *file << "\tunit = " << (World::getInstance().getConfig()->GetIsAngstroem() ? "angstrom" : "bohr" ) << endl;
    *file << "\t{ atoms geometry } = {" << endl;
    // output of atoms
    BOOST_FOREACH(const atom *_atom, atoms) {
      OutputMPQCLine(file, *_atom, ¢er);
    }
    *file << "\t}" << endl;
    *file << ")" << endl;
    *file << "basis: (" << endl;
    *file << "\tname = \"" << getParams().getParameter(MpqcParser_Parameters::basisParam) << "\"" << endl;
    *file << "\tmolecule = $:molecule" << endl;
    *file << ")" << endl;
    if (theory == getParams().getTheoryName(MpqcParser_Parameters::MBPT2_R12)) {
      *file << "% auxiliary basis set specification" << endl;
      *file << "\tabasis: (" << endl;
      *file << "\tname = \"" << getParams().getParameter(MpqcParser_Parameters::aux_basisParam) << "\"" << endl;
      *file << "\tmolecule = $:molecule" << endl;
      *file << ")" << endl;
    }
  }
}