/*
 * 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 joiner.cpp
 *
 * Takes evaluated fragments (energy and forces) and by reading the factors files determines total energy
 * and each force for the whole molecule.
 *
 */
//============================ INCLUDES ===========================
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include "datacreator.hpp"
#include "Element/periodentafel.hpp"
#include "Fragmentation/defs.hpp"
#include "Fragmentation/EnergyMatrix.hpp"
#include "Fragmentation/ForceMatrix.hpp"
#include "Fragmentation/helpers.hpp"
#include "Fragmentation/HessianMatrix.hpp"
#include "Fragmentation/KeySetsContainer.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
//============================== MAIN =============================
int main(int argc, char **argv)
{
  periodentafel *periode = NULL; // and a period table of all elements
  EnergyMatrix Energy;
  EnergyMatrix EnergyFragments;
  
  EnergyMatrix Hcorrection;
  EnergyMatrix HcorrectionFragments;
  
  ForceMatrix Force;
  ForceMatrix ForceFragments;
  HessianMatrix Hessian;
  HessianMatrix HessianFragments;
  
  ForceMatrix Shielding;
  ForceMatrix ShieldingPAS;
  ForceMatrix ShieldingFragments;
  ForceMatrix ShieldingPASFragments;
  ForceMatrix Chi;
  ForceMatrix ChiPAS;
  ForceMatrix ChiFragments;
  ForceMatrix ChiPASFragments;
  KeySetsContainer KeySet;  
  stringstream prefix;
  char *dir = NULL;
  bool NoHCorrection = false;
  bool NoHessian = false;
  LOG(0, "Joiner");
  LOG(0, "======");
  // Get the command line options
  if (argc < 3) {
    LOG(0, "Usage: " << argv[0] << "   [elementsdb]");
    LOG(0, "\ttherein the output of a molecuilder fragmentation is expected, each fragment with a subdir containing an energy.all and a forces.all file.");
    LOG(0, "\tprefix of energy and forces file.");
    LOG(0, "[elementsdb]\tpath to elements database, needed for shieldings.");
    return 1;
  } else {
    dir = new char[strlen(argv[2]) + 2];
    strcpy(dir, "/");
    strcat(dir, argv[2]);
  }
  if (argc > 3) {
    periode = new periodentafel;
    periode->LoadPeriodentafel(argv[3]);
  }
  // Test the given directory
  if (!TestParams(argc, argv))
    return 1;
  // +++++++++++++++++ PARSING +++++++++++++++++++++++++++++++
  // ------------- Parse through all Fragment subdirs --------
  if (!Energy.ParseFragmentMatrix(argv[1], dir, EnergySuffix, 0,0)) return 1;
  if (!Hcorrection.ParseFragmentMatrix(argv[1], "", HCORRECTIONSUFFIX, 0,0)) {
    NoHCorrection = true;
    LOG(0, "No HCorrection matrices found, skipping these.");
  }
  if (!Force.ParseFragmentMatrix(argv[1], dir, ForcesSuffix, 0,0)) return 1;
  if (!Hessian.ParseFragmentMatrix(argv[1], dir, HessianSuffix, 0,0)) {
    NoHessian = true;
    LOG(0, "No hessian matrices found, skipping these.");
  }
  if (periode != NULL) { // also look for PAS values
    if (!Shielding.ParseFragmentMatrix(argv[1], dir, ShieldingSuffix, 1, 0)) return 1;
    if (!ShieldingPAS.ParseFragmentMatrix(argv[1], dir, ShieldingPASSuffix, 1, 0)) return 1;
    if (!Chi.ParseFragmentMatrix(argv[1], dir, ChiSuffix, 1, 0)) return 1;
    if (!ChiPAS.ParseFragmentMatrix(argv[1], dir, ChiPASSuffix, 1, 0)) return 1;
  }
  // ---------- Parse the TE Factors into an array -----------------
  if (!Energy.InitialiseIndices()) return 1;
  if (!NoHCorrection) 
    Hcorrection.InitialiseIndices();
  
  // ---------- Parse the Force indices into an array ---------------
  if (!Force.ParseIndices(argv[1])) return 1;
  // ---------- Parse the Hessian (=force) indices into an array ---------------
  if (!NoHessian)
    if (!Hessian.InitialiseIndices((class MatrixContainer *)&Force)) return 1;
  // ---------- Parse the shielding indices into an array ---------------
  if (periode != NULL) { // also look for PAS values
    if(!Shielding.ParseIndices(argv[1])) return 1;
    if(!ShieldingPAS.ParseIndices(argv[1])) return 1;
    if(!Chi.ParseIndices(argv[1])) return 1;
    if(!ChiPAS.ParseIndices(argv[1])) return 1;
  }
  // ---------- Parse the KeySets into an array ---------------
  if (!KeySet.ParseKeySets(argv[1], Force.RowCounter, Force.MatrixCounter)) return 1;
  if (!KeySet.ParseManyBodyTerms()) return 1;
  if (!EnergyFragments.AllocateMatrix(Energy.Header, Energy.MatrixCounter, Energy.RowCounter, Energy.ColumnCounter)) return 1;
  if (!NoHCorrection)  
    HcorrectionFragments.AllocateMatrix(Hcorrection.Header, Hcorrection.MatrixCounter, Hcorrection.RowCounter, Hcorrection.ColumnCounter);
  if (!ForceFragments.AllocateMatrix(Force.Header, Force.MatrixCounter, Force.RowCounter, Force.ColumnCounter)) return 1;
  if (!NoHessian)
    if (!HessianFragments.AllocateMatrix(Hessian.Header, Hessian.MatrixCounter, Hessian.RowCounter, Hessian.ColumnCounter)) return 1;
  if (periode != NULL) { // also look for PAS values
    if (!ShieldingFragments.AllocateMatrix(Shielding.Header, Shielding.MatrixCounter, Shielding.RowCounter, Shielding.ColumnCounter)) return 1;
    if (!ShieldingPASFragments.AllocateMatrix(ShieldingPAS.Header, ShieldingPAS.MatrixCounter, ShieldingPAS.RowCounter, ShieldingPAS.ColumnCounter)) return 1;
    if (!ChiFragments.AllocateMatrix(Chi.Header, Chi.MatrixCounter, Chi.RowCounter, Chi.ColumnCounter)) return 1;
    if (!ChiPASFragments.AllocateMatrix(ChiPAS.Header, ChiPAS.MatrixCounter, ChiPAS.RowCounter, ChiPAS.ColumnCounter)) return 1;
  }
  // ----------- Resetting last matrices (where full QM values are stored right now)
  if(!Energy.SetLastMatrix(0., 0)) return 1;
  if(!Force.SetLastMatrix(0., 2)) return 1;
  if (!NoHessian)
    if (!Hessian.SetLastMatrix(0., 0)) return 1;
  if (periode != NULL) { // also look for PAS values
    if(!Shielding.SetLastMatrix(0., 2)) return 1;
    if(!ShieldingPAS.SetLastMatrix(0., 2)) return 1;
    if(!Chi.SetLastMatrix(0., 2)) return 1;
    if(!ChiPAS.SetLastMatrix(0., 2)) return 1;
  }
  // +++++++++++++++++ SUMMING +++++++++++++++++++++++++++++++
  // --------- sum up and write for each order----------------
  for (int BondOrder=0;BondOrder