#include <gtest/gtest.h>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>

#include "helm.h"
#include "resourceManager.h"
#include "config.h"

/**
 * @file constTest.cpp
 * @brief Unit tests for the const class.
 */

/**
 * @brief Test suite for the const class.
 */
class eosTest : public ::testing::Test {};

std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/testsConfig.yaml";

/**
 * @test Verify default constructor initializes correctly.
 */

TEST_F(eosTest, read_helm_table) {
    serif::config::Config::getInstance().loadConfig(TEST_CONFIG);
    serif::resource::ResourceManager& rm = serif::resource::ResourceManager::getInstance();
    auto& eos = std::get<std::unique_ptr<serif::eos::EOSio>>(rm.getResource("eos:helm"));
    auto& table = eos->getTable();
    auto& helmTable = *std::get<std::unique_ptr<serif::eos::helmholtz::HELMTable>>(table);
    std::stringstream ss;
    ss << helmTable;
    EXPECT_EQ(ss.str(), "HELMTable Data:\n  imax: 541, jmax: 201\n  Temperature Range: [1000, 1e+13]\n  Density Range: [1e-12, 1e+15]\n");
}

TEST_F(eosTest, get_helm_EOS) {

    const int nel=3;
    double xmass[nel], aion[nel], zion[nel];
    serif::eos::helmholtz::EOSInput eos1;

    xmass[0] = 0.75; aion[0] =  1.0; zion[0] = 1.0;
    xmass[1] = 0.23; aion[1] =  4.0; zion[1] = 2.0; 
    xmass[2] = 0.02; aion[2] = 12.0; zion[2] = 6.0;

    eos1.T = 1.0e8;
    eos1.rho = 1.0e6;

    double asum = 0.0;
    double zsum = 0.0;
    for (int i=0; i<nel; i++) {
        asum += xmass[i]/aion[i];
        zsum += xmass[i]*zion[i]/aion[i];
    }
    eos1.abar = 1.0/asum;
    eos1.zbar = eos1.abar*zsum;

    serif::resource::ResourceManager& rm = serif::resource::ResourceManager::getInstance();
    auto& eos = std::get<std::unique_ptr<serif::eos::EOSio>>(rm.getResource("eos:helm"));
    auto& table = eos->getTable();
    auto& helmTable = *std::get<std::unique_ptr<serif::eos::helmholtz::HELMTable>>(table);
    serif::eos::helmholtz::EOS helmEos = get_helm_EOS(eos1, helmTable);

    const double absErr = 1e-12;

    //Check composition info
    EXPECT_NEAR(     helmEos.ye, 8.75e-01, absErr);

    //Check E, P, S and derivatives of each wrt Rho and T
    EXPECT_NEAR( helmEos.etaele, 2.3043348231021554e+01, absErr);
    EXPECT_NEAR(   helmEos.etot, 1.1586558190936826e+17, 1e3);
    EXPECT_NEAR(helmEos.denerdd, 6.1893000468285858e+10, 1e-2);
    EXPECT_NEAR(helmEos.denerdt, 1.2129708972542575e+08, 1e-7);
    EXPECT_NEAR(   helmEos.ptot, 6.9610135220017030e+22, 1e10);
    EXPECT_NEAR(helmEos.dpresdd, 1.0296440482849070e+17, 1e3);
    EXPECT_NEAR(helmEos.dpresdt, 7.7171347517311625e+13, 1.0);
    EXPECT_NEAR(   helmEos.stot, 6.0647461970567346e+08, 1e-7);
    EXPECT_NEAR(helmEos.dentrdd,-7.7171347517311645e+01, absErr);
    EXPECT_NEAR(helmEos.dentrdt, 1.2129708972542577e+00, absErr);

    // Maxwell relations, should always be zero
    EXPECT_NEAR(    helmEos.dse,              0, absErr);
    EXPECT_NEAR(    helmEos.dpe,              0, absErr);
    EXPECT_NEAR(    helmEos.dsp,              0, absErr);
}
