/*
 * Copyright University of Reims Champagne-Ardenne
 * Authors and Contributors: Akilan RAJAMANI, Corentin LEFEBVRE, Johanna KLEIN,
 *                           Emmanuel PLUOT, Gaetan RUBEZ, Hassan KHARTABIL,
 *                           Jean-Charles BOISSON and Eric HENON
 * (24/07/2017)
 * jean-charles.boisson@univ-reims.fr, eric.henon@univ-reims.fr
 *
 * This software is a computer program whose purpose is to
 * detect and quantify interactions from electron density
 * obtained either internally from promolecular density or
 * calculated from an input wave function input file. It also
 * prepares for the visualization of isosurfaces representing
 * several descriptors (dg) coming from the IGM methodology.
 *
 * This software is governed by the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL-C
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C license and that you accept its terms.
 *
 * */

/**
 * @file WFreader.h
 * @brief Procedure/Function to manage wave function files (WFN V1.0 and WFX V2.0)
 */

#ifndef _WFNREADER_H_
//! For managing C header inclusion for WFreader.h
#define _WFNREADER_H_


// STL
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <general.h>

// ADF files management
#include <ADF_KFReader.h>

//! structure to save center data
typedef struct centerData
  {
    //! The atomic element, using a string for easing the use
    std::string* element;
    //! to doc and rename
    unsigned int value;
    //! to doc
    unsigned int rank;
    //! coordinates to doc and rename
    double coordinates[3];
    //! to doc
    double charge;
  } centerData;

//! structure to save molecule orbital data
typedef struct moleculeOrbital
  {
    //! to doc and rename
    unsigned int rank;
    //! to doc and rename
    double data2;
    //! TO DOCUMENT
    double occupancy;
    //! to doc
    double orbitalEnergy;
    //! Primites coefficient for one molecular orbital
    double* coefficients;
  } moleculeOrbital;

/**
 * @fn void wf_load_file(const std::string & fileName, const bool verbose=false);
 * @brief Tool function which read the corresponding file
 * @param fileName The name of the file to read
 * @param verbose If some debug information have to be printed */
void wf_load_file(const std::string & fileName, const bool verbose=false);

/**
 * @fn void wfn_load_file(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which load the corresponding wfn file
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_load_file(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_read_GAUSSIAN_line(const std::string & line, const bool verbose=false)
 * @brief Tool function which read the line starting by GAUSSIAN to extract the number of orbitals, of primitives and nuclei
 * @param line The line to considered
 * @param verbose If some debug information have to be printed */
void wfn_read_GAUSSIAN_line(const std::string & line, const bool verbose=false);

/**
 * @fn void wfn_read_center_data(std::ifstream & inputFile, const bool verbose=false)
 * @brief Tool function which read the center data according to the number of nuclei
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_read_center_data(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_read_primitive_centers(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which read the primitive centers (multiple lines according to the number of primitives)
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_read_primitive_centers(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_read_primitive_types(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which read the primitive types (multiple lines according to the number of primitives)
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_read_primitive_types(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_read_exponents(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which read the exponents (multiple lines according to the number of primitives)
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_read_exponents(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_read_molecule_orbitals(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which read the molecule orbital (multiple lines according to the number of primitives)
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_read_molecule_orbitals(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_read_total_energy_and_virial(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which read the total energy and associated virial
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfn_read_total_energy_and_virial(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfn_fromFortranRealToCppReal(std::string & line)
 * @brief Tool function which allows to transform a line with fortran real (with D) to C real (with E) in order to read them
 * @param line The string containing the information to transform */
void wfn_fromFortranRealToCppReal(std::string & line);

/**
 * @fn void wfn_printTotalEnergyAndVirial()
 * @brief Tool procedure to print the total energy and virial (only use with WFN reading for the moment*/
void wfn_printTotalEnergyAndVirial();


/**
 * @fn void wfx_load_file(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which load the corresponding wfx file
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfx_load_file(std::ifstream & inputFile, const bool verbose=false);

/**
 * @fn void wfx_init()
 * @brief Tool procedure to initialize some stuff for wfx reading.*/
void wfx_init();

/**
 * @fn void wfx_read_data(std::ifstream & inputFile, const bool verbose=false);
 * @brief Tool function which read the data of the corresponding wfx file (this method must be called after wfx_load)
 * @param inputFile The stream in order to read the data
 * @param verbose If some debug information have to be printed */
void wfx_read_data(std::ifstream & inputFile, const bool verbose=false);


/**
 * @fn void wf_cleaning(const bool verbose=false)
 * @brief Tool function which frees all the allocated memory */
void wf_cleaning(const bool verbose=false);


/**
 * @fn bool wf_file_already_loaded()
 * @brief Tool function which allows to know if a file has already loaded
 * @return If a file is already loaded or not */
bool wf_file_already_loaded();

/**
 * @fn unsigned int wf_number_of_orbitals()
 * @brief Tool function which allows to know the number of orbitals (if a file is loaded)
 * @return The number of orbitals */
unsigned int wf_number_of_orbitals();

/**
 * @fn unsigned int wf_number_of_primitives()
 * @brief Tool function which allows to know the number of primitives (if a file is loaded)
 * @return The number of primitives */
unsigned int wf_number_of_primitives();

/**
 * @fn unsigned int wf_number_of_nuclei()
 * @brief Tool function which allows to know the number of nuclei (if a file is loaded)
 * @return The number of nuclei */
unsigned int wf_number_of_nuclei();

/**
 * @fn unsigned int* wf_user2ADF_atomOrder()
 * @brief Tool function which return the array storing the atom re-ordering (from user to ADF index)
 * @return user2ADF_atomOrder array */
unsigned int* wf_user2ADF_atomOrder();

/**
 * @fn unsigned int* wf_ADF2user_atomOrder()
 * @brief Tool function which return the array storing the atom re-ordering (from ADF to user index)
 * @return ADF2user_atomOrder array */
unsigned int* wf_ADF2user_atomOrder();


/**
 * @fn centerData* wf_center_data()
 * @brief Tool function which allows to gain access to all the center data (if a file is loaded)
 * @return The center data */
centerData* wf_center_data();

/**
 * @fn unsigned int* wf_primitive_centers()
 * @brief Tool function which allows to gain access to all the primitive centers (if a file is loaded)
 * @return The primitive centers */
unsigned int* wf_primitive_centers();

/**
 * @fn unsigned int* wf_primitive_types()
 * @brief Tool function which allows to gain access to all the primitive types (if a file is loaded)
 * @return The primitive types */
unsigned int* wf_primitive_types();

/**
 * @fn double* wf_primitive_exponents()
 * @brief Tool function which allows to gain access to all the exponents (if a file is loaded)
 * @return The exponents */
double* wf_primitive_exponents();

/**
 *  * @fn int wf_netCharge()
 *  * @brief Tool function which returns the net charge of the whole system (if a file is loaded)
 *  * @return The Net Charge */
int wf_netCharge();

/**
 *  * @fn void wf_setNetCharge()
 *  * @brief Tool function which set the net charge of the whole system (if a file is loaded) */
void wf_setNetCharge(int value);

/**
 * @fn double* wf_molecular_orbitals()
 * @brief Tool function which allows to gain access to all molecular orbitals (if a file is loaded)
 * @return The molecular orbitals */
std::vector<moleculeOrbital> 
wf_molecular_orbitals();

/**
 *  * @fn bool wf_molecular_orbitals_sort();
 *  * @brief Tool function which sort the MOs by ascending order of energy
 *  * @return true if MO order has changed  */
bool 
wf_molecular_orbitals_sort();

/**
 * @fn void wf_allocateMemory()
 * @brief Tool procedure to allocate the memory for the array linked to the number of nuclei, centers and primitives.*/
void wf_allocateMemory();

/**
 * @fn void wf_printGlobalBounds()
 * @brief Tool procedure to print 3 importants values : the number of nuclei, of primitives and molecular orbitals*/
void wf_printGlobalBounds();

/**
 * @fn void wf_printMO()
 * @brief Tool procedure to print molecular orbitals information */
void wf_printMO();

/**
 * @fn void wf_printPrimitiveExponents()
 * @brief Tool procedure to print the primitive exponents*/
void wf_printPrimitiveExponents();

/**
 * @fn void wf_printPrimitiveTypes()
 * @brief Tool procedure to print the primitive types*/
void wf_printPrimitiveTypes();

/**
 * @fn void wf_printPrimitiveCenters()
 * @brief Tool procedure to print the primitive centers*/
void wf_printPrimitiveCenters();

/**
 * @fn void wf_printCenterData();
 * @brief Tool procedure to print the center data*/
void wf_printCenterData();

/**
 * @fn template <typename T> void loadInputFileData(std::ifstream & inputFile, T *data, unsigned int nbData)
 * @brief Template Tool procedure to load the data from an inputFile. 
 * @warning The reading assumes that the inputFile is ready to read nbData T values... No verification
 * @param inputFile The stream in order to read the data
 * @param data The array of T to load
 * @param nbData The number of values of type T to read */
template <typename T> void loadInputFileData(std::ifstream & inputFile, T *data, unsigned int nbData)
{
  unsigned int nbDataRead=0;

  while(nbDataRead != nbData)
    {
      inputFile >> data[nbDataRead++];
    }
}

/**
 * @fn template <typename T> void wf_printInformation(const T *data, const unsigned int nbData, const unsigned int nbDataLine,const unsigned int borderShift=1,const bool scientific=true, const unsigned int precision=12)
 * @brief Template Tool procedure to print data
 * @param data The array of T to print
 * @param nbData The number of values in data
 * @param nbDataLine The number of data by line
 * @param borderShift The number of shift ("\t") to put at the beginning of a line
 * @param scientific Activate the scientific notation ?
 * @param precision The precision is scientific notation is required (not used if scientific is not true) */
template <typename T> void wf_printInformation(const T *data, const unsigned int nbData, const unsigned int nbDataLine,const unsigned int borderShift=1,const bool scientific=true, const unsigned int precision=12)
{
  if(scientific)
    {
      std::cout << std::scientific << std::setprecision(precision);
    }
  
  for(unsigned int i(0);i<nbData;++i)
    {
      if(i%nbDataLine==0)
	{
	  std::cout << std::endl;
	  for(unsigned int shift(0);shift<borderShift;++shift)
	    {
	      std::cout << "\t";
	    }
	}

      // JC 2.6.22 update : as data[i] is always a numerical information, cast to double for avoiding "pointless comparison of unsigned integer with zero" warning from PGI
      if(((double)data[i])>=0.0)
	{
	  std::cout << " ";
	}
      std::cout << data[i] << " ";
    }
  std::cout << std::endl;
}

/**
 * @fn string removeSpaces(string str)
 * @brief Function to remove extra spaces in a string
 * @return The string without spaces  
 */
std::string removeSpaces(std::string str);

/**
 * @fn bool isDouble(std::string text)
 * @brief Function checking if a string is a number  
 * @return true is the textis a number
 */
bool isDouble(std::string text);


/**
 * @fn bool toDouble(std::string text)
 * @brief Function converting a string to a double
 * @return a double number or 0.0 if the conversion failed
 */
double toDouble(std::string text);


/********************************************************************************
 * For RKF reading *
 ********************************************************************************/

void load_rkf_file(const std::string & fileName);

void rkf_load_nbNuclei(KFFile* kf);

void rkf_load_data(KFFile* kf, const char *filename);

/**
 * @fn unsigned int* wf_primitive_kr()
 * @brief TO DOC
 * @return The primitive kr */
unsigned int* wf_primitive_krs();


#endif
