#include <iostream>
#include "param_loader.h"

parameter_loader::parameter_loader()
{
    lua.open_libraries(sol::lib::base, sol::lib::math);
    init();
}

void
parameter_loader::init(void)
{
    lua["const"] = lua.create_table();
    lua["const"]["eps0"] = 8.8541878128e-12;
    lua["const"]["mu0"] = 4e-7*M_PI;

    lua["sim"] = lua.create_table();
    lua["sim"]["approx_order"] = 1;
    lua["sim"]["geom_order"] = 1;
    lua["sim"]["use_gpu"] = 0;
    //lua["sim"]["name"];
    //lua["sim"]["dt"];
    //lua["sim"]["timesteps"];
    //lua["sim"]["gmsh_model"];

    lua["materials"] = lua.create_table();
    lua["bndconds"] = lua.create_table();
    lua["ifaceconds"] = lua.create_table();

    lua["postpro"] = lua.create_table();
    lua["postpro"]["silo_output_rate"] = 0;
}

bool
parameter_loader::validate_simulation_params(void) const
{
    bool success = true;

    auto sim_name = lua["sim"]["name"];
    if (not sim_name.valid())
    {
        std::cout << "[CONFIG] 'sim.name' not set" << std::endl;
        success = false;
    } 

    auto sim_dt = lua["sim"]["dt"];
    if (not sim_dt.valid())
    {
        std::cout << "[CONFIG] 'sim.dt' not set" << std::endl;
        success = false;
    } 

    auto sim_timesteps = lua["sim"]["timesteps"];
    if (not sim_timesteps.valid())
    {
        std::cout << "[CONFIG] 'sim.timesteps' not set" << std::endl;
        success = false;
    } 

    auto sim_gmshmodel = lua["sim"]["gmsh_model"];
    if (not sim_gmshmodel.valid())
    {
        std::cout << "[CONFIG] 'sim.gmsh_model' not set" << std::endl;
        success = false;
    } 

    return success;
}

bool
parameter_loader::load_file(const std::string& fn)
{
    auto script = lua.script_file(fn);
    if (not script.valid())
        return false;

    return validate_simulation_params();
}

int
parameter_loader::approx_order(void) const
{
    return lua["sim"]["approx_order"];
}

int
parameter_loader::geom_order(void) const
{
    return lua["sim"]["geom_order"];
}

double
parameter_loader::delta_t(void) const
{
    return lua["sim"]["dt"];
}

size_t
parameter_loader::timesteps(void) const
{
    return lua["sim"]["timesteps"];
}

std::string
parameter_loader::simulation_name(void) const
{
    return lua["sim"]["name"];
}

bool
parameter_loader::use_gpu(void) const
{
    return lua["sim"]["use_gpu"];
}

size_t
parameter_loader::silo_output_rate(void) const
{
    return lua["postpro"]["silo_output_rate"];
}

maxwell_parameter_loader::maxwell_parameter_loader()
{}

bool
maxwell_parameter_loader::validate_materials(const std::string& mpn, int tag)
{
    auto mfun = lua["materials"][mpn];
    if ( mfun.valid() )
        return true;

    auto mparams = lua["materials"][tag];
    if ( mparams.valid() )
    {
        auto matparams_mpn = lua["materials"][tag][mpn];
        if ( matparams_mpn.valid() )
            return true;
    }

    std::cout << "[CONFIG] 'materials[" << tag << "]." << mpn << "' not defined and ";
    std::cout << "'materials." << mpn << "(tag,x,y,z)' not present." << std::endl;
    return false;
}

bool
maxwell_parameter_loader::validate_model_params(const model& mod)
{
    bool success = true;
    for (auto& e : mod)
    {
        auto tag = e.gmsh_tag();
        bool eps_valid = validate_materials("epsilon", tag);
        bool mu_valid = validate_materials("mu", tag);
        bool sigma_valid = validate_materials("sigma", tag);
        if ( (not eps_valid) or (not mu_valid) or (not sigma_valid) )
            success = false;
    }

    return success;
}

bool
maxwell_parameter_loader::initial_Efield_defined(void) const
{
    auto eic = lua["electric_initial_condition"];
    return eic.valid();
}

vec3d
maxwell_parameter_loader::Efield(const point_3d& pt) const
{
    vec3d ret;
    sol::tie(ret(0), ret(1), ret(2)) =
        lua["electric_initial_condition"](pt.x(), pt.y(), pt.z());
    return ret;
}

bool
maxwell_parameter_loader::initial_Hfield_defined(void) const
{
    auto mic = lua["magnetic_initial_condition"];
    return mic.valid();
}

vec3d
maxwell_parameter_loader::Hfield(const point_3d& pt) const
{
    vec3d ret;
    sol::tie(ret(0), ret(1), ret(2)) =
        lua["magnetic_initial_condition"](pt.x(), pt.y(), pt.z());
    return ret;
}