/*
 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#define BOOST_TEST_MAIN

#define BOOST_TEST_MODULE aoshell_test

// Standard includes
#include <fstream>

// Third party includes
#include <boost/test/unit_test.hpp>

// Local VOTCA includes
#include "votca/xtp/aobasis.h"
#include "votca/xtp/aoshell.h"
#include "votca/xtp/orbitals.h"
#include "votca/xtp/orbreorder.h"
#include "xtp_libint2.h"
using namespace votca::xtp;
using namespace std;

BOOST_AUTO_TEST_SUITE(aoshell_test)

BOOST_AUTO_TEST_CASE(EvalAOspace) {
  libint2::initialize();
  QMMolecule mol = QMMolecule("", 0);
  mol.LoadFromFile(std::string(XTP_TEST_DATA_FOLDER) + "/aoshell/Al.xyz");
  BasisSet basis;
  basis.Load(std::string(XTP_TEST_DATA_FOLDER) + "/aoshell/largeshell.xml");
  AOBasis aobasis;
  aobasis.Fill(basis, mol);

  Eigen::MatrixXd aoval_ref = Eigen::MatrixXd::Zero(aobasis.AOBasisSize(), 1);
  aoval_ref << 0.0680316, 0.0895832, 0.0895832, 0.0895832, 0, 0.126153,
      0.126153, 0.126153, 0, -0.102376, 0.0626925, 0.0626925, 0.198251, 0,
      0.0809357, -0.0809357, -0.122215, -0.0552111, -0.0552111, 0.156161, 0,
      0.146075, -0.146075, 0, -0.103291;

  Eigen::MatrixXd aograd_ref = Eigen::MatrixXd::Zero(aobasis.AOBasisSize(), 3);

  aograd_ref << -0.057521967096, -0.057521967096, -0.057521967096,
      -0.091846116024, -0.091846116024, -0.0022629076774, -0.091846116024,
      -0.0022629076774, -0.091846116024, -0.0022629076774, -0.091846116024,
      -0.091846116024, -0.072834589679, -0.072834589679, 0.14566917936,
      -0.16812154688, -0.041968337007, -0.041968337007, -0.041968337007,
      -0.16812154688, -0.041968337007, -0.041968337007, -0.041968337007,
      -0.16812154688, 0.12615320987, -0.12615320987, 0, 0.027261217239,
      0.027261217239, 0.18082590591, -0.17342532206, -0.11073280044,
      0.14003728606, -0.11073280044, -0.17342532206, 0.14003728606,
      -0.15191670048, -0.15191670048, -0.15191670048, 0.19825116059,
      -0.19825116059, 0, 0.099851661525, -0.14295543066, -0.14295543066,
      0.14295543066, -0.099851661525, 0.14295543066, 0.16861461687,
      0.16861461687, -0.0059782842903, -0.042137230049, -0.097348353145,
      0.28912950853, -0.097348353145, -0.042137230049, 0.28912950853,
      -0.27121951095, -0.27121951095, 0.11918208443, 0.15616063815,
      -0.15616063815, 0, 0.11148463165, -0.32674007231, -0.18066517099,
      0.32674007231, -0.11148463165, 0.18066517099, 0.20658110657,
      -0.20658110657, 0, 0.024459014248, 0.024459014248, 0.23104012081;

  // clang-format off
    std::array<votca::Index, 49> votcaOrder_old = {
        0,                             // s
        0, -1, 1,                      // p
        0, -1, 1, -2, 2,               // d
        0, -1, 1, -2, 2, -3, 3,        // f
        0, -1, 1, -2, 2, -3, 3, -4, 4,  // g
        0, -1, 1, -2, 2, -3, 3, -4, 4,-5,5,  // h
        0, -1, 1, -2, 2, -3, 3, -4, 4,-5,5,-6,6  // i
    };
  // clang-format on

  std::array<votca::Index, 49> multiplier;
  multiplier.fill(1);
  OrbReorder ord(votcaOrder_old, multiplier);
  ord.reorderOrbitals(aograd_ref, aobasis);
  ord.reorderOrbitals(aoval_ref, aobasis);

  for (const AOShell& shell : aobasis) {

    Eigen::Vector3d gridpos = Eigen::Vector3d::Ones();

    AOShell::AOValues ao = shell.EvalAOspace(gridpos);

    ao.derivatives.middleRows(0, 1);

    bool ao_check = aoval_ref.col(0)
                        .segment(shell.getStartIndex(), shell.getNumFunc())
                        .isApprox(ao.values, 1e-5);
    if (!ao_check) {
      std::cout << shell << std::endl;
      std::cout << "ref" << std::endl;
      std::cout << aoval_ref.col(0).segment(shell.getStartIndex(),
                                            shell.getNumFunc())
                << std::endl;
      std::cout << "result" << std::endl;
      std::cout << ao.values << std::endl;
    }
    BOOST_CHECK_EQUAL(ao_check, 1);
    bool aograd_check =
        aograd_ref.middleRows(shell.getStartIndex(), shell.getNumFunc())
            .isApprox(ao.derivatives, 1e-5);
    if (!aograd_check) {
      std::cout << shell << std::endl;
      std::cout << "ref" << std::endl;
      std::cout << aograd_ref.block(shell.getStartIndex(), 0,
                                    shell.getNumFunc(), 3)
                << std::endl;
      std::cout << "result" << std::endl;
      std::cout << ao.derivatives << std::endl;
    }

    BOOST_CHECK_EQUAL(aograd_check, 1);
  }
  libint2::finalize();
}

BOOST_AUTO_TEST_SUITE_END()
