#ifdef WITH_LAPACKE
#include "lapacke_wrapper.h"

#include <cstring>
#include <iostream>

/**
 * @brief Check status of calling lapacke functions.
 *
 * @param[in] caller name of lapacke function
 * @param[in] info status of lapacke function
 */
static void check_info(const std::string& caller, const lapack_int& info)
{
    if (info != 0) {
        std::cerr << "ERROR: " << caller << " failed with info = " << info << std::endl;
        std::exit(-1);
    }
}

// Treated as C to avoid name mangling
extern "C" {

void lapacke_zheev_wrapper(
    lapack_int num_orb,
    lapack_complex_double* hk,
    double* eigenvalues,
    lapack_complex_double* eigenvectors,
    bool calc_eigenvectors)
{
    // local variables
    std::string jobz = calc_eigenvectors ? "V" : "N";
    std::string uplo = "U";

    // Call lapacke and check status
    lapack_int info = LAPACKE_zheev(
        LAPACK_COL_MAJOR,
        jobz.at(0), uplo.at(0),
        num_orb, hk, num_orb,
        eigenvalues);
    check_info("LAPACKE_zheev", info);

    // Copy eigenvectors
    if (calc_eigenvectors) {
        std::memcpy(eigenvectors, hk, num_orb * num_orb * sizeof(lapack_complex_double));
    }
}

void lapacke_zhegv_wrapper(
    lapack_int num_orb,
    lapack_complex_double* hk,
    lapack_complex_double* sk,
    double* eigenvalues,
    lapack_complex_double* eigenvectors,
    bool calc_eigenvectors)
{
    // local variables
    std::string jobz = calc_eigenvectors ? "V" : "N";
    std::string uplo = "U";

    // Call lapacke and check status
    lapack_int info = LAPACKE_zhegv(
        LAPACK_COL_MAJOR,
        1, jobz.at(0), uplo.at(0),
        num_orb, hk, num_orb,
        sk, num_orb,
        eigenvalues);
    check_info("LAPACKE_zhegv", info);

    // Copy eigenvectors
    if (calc_eigenvectors) {
        std::memcpy(eigenvectors, hk, num_orb * num_orb * sizeof(lapack_complex_double));
    }
}

void lapacke_zgeev_wrapper(
    lapack_int num_orb,
    lapack_complex_double* dk,
    lapack_complex_double* eigenvalues)
{
    // Local variables
    std::string jobz = "N";
    lapack_int n = num_orb;
    lapack_int lda = num_orb;
    lapack_int ldvl = num_orb;
    lapack_int ldvr = num_orb;
    lapack_int info = 0;

    // // Dummy arrays since we do not compute the eigenvectors.
    // lapack_complex_double vl[16];
    // lapack_complex_double vr[16];

    // Call lapacke and check status
    // Reference:
    // https://www.intel.com/content/www/us/en/docs/onemkl/code-samples-lapack/2022-1/zgeev-example-c.html
    info = LAPACKE_zgeev(
        LAPACK_COL_MAJOR,
        jobz.at(0), jobz.at(0),
        n, dk, lda,
        eigenvalues,
        nullptr, ldvl,
        nullptr, ldvr);
    check_info("LAPACKE_zgeev", info);
}

} // extern "C"
#endif // WITH_LAPACKE