#include "writer.h"

// Silencing build warnings from original code
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif

Writer::Writer(const Settings &settings, const GrayScott &sim, adios2::IO io)
    : settings(settings), io(io)
{
    io.DefineAttribute<double>("F", settings.F);
    io.DefineAttribute<double>("k", settings.k);
    io.DefineAttribute<double>("dt", settings.dt);
    io.DefineAttribute<double>("Du", settings.Du);
    io.DefineAttribute<double>("Dv", settings.Dv);
    io.DefineAttribute<double>("noise", settings.noise);

    // add attributes for Fides
    io.DefineAttribute<std::string>("Fides_Data_Model", "uniform");
    double origin[3] = {0.0, 0.0, 0.0};
    io.DefineAttribute<double>("Fides_Origin", &origin[0], 3);
    double spacing[3] = {0.1, 0.1, 0.1};
    io.DefineAttribute<double>("Fides_Spacing", &spacing[0], 3);
    io.DefineAttribute<std::string>("Fides_Dimension_Variable", "U");

    std::vector<std::string> varList = {"U", "V"};
    std::vector<std::string> assocList = {"points", "points"};
    io.DefineAttribute<std::string>("Fides_Variable_List", varList.data(), varList.size());
    io.DefineAttribute<std::string>("Fides_Variable_Associations", assocList.data(), assocList.size());

    var_u =
        io.DefineVariable<double>("U", {settings.L, settings.L, settings.L},
                                  {sim.offset_z, sim.offset_y, sim.offset_x},
                                  {sim.size_z, sim.size_y, sim.size_x});

    var_v =
        io.DefineVariable<double>("V", {settings.L, settings.L, settings.L},
                                  {sim.offset_z, sim.offset_y, sim.offset_x},
                                  {sim.size_z, sim.size_y, sim.size_x});

    if (settings.adios_memory_selection) {
        var_u.SetMemorySelection(
            {{1, 1, 1}, {sim.size_z + 2, sim.size_y + 2, sim.size_x + 2}});
        var_v.SetMemorySelection(
            {{1, 1, 1}, {sim.size_z + 2, sim.size_y + 2, sim.size_x + 2}});
    }

    var_step = io.DefineVariable<int>("step");
}

void Writer::open(const std::string &fname)
{
    writer = io.Open(fname, adios2::Mode::Write);
}

void Writer::write(int step, const GrayScott &sim)
{
    if (settings.adios_memory_selection) {
        const std::vector<double> &u = sim.u_ghost();
        const std::vector<double> &v = sim.v_ghost();

        writer.BeginStep();
        writer.Put<int>(var_step, &step);
        writer.Put<double>(var_u, u.data());
        writer.Put<double>(var_v, v.data());
        writer.EndStep();
    } else if (settings.adios_span) {
        writer.BeginStep();

        writer.Put<int>(var_step, &step);

        // provide memory directly from adios buffer
        adios2::Variable<double>::Span u_span = writer.Put<double>(var_u);
        adios2::Variable<double>::Span v_span = writer.Put<double>(var_v);

        // populate spans
        sim.u_noghost(u_span.data());
        sim.v_noghost(v_span.data());

        writer.EndStep();
    } else {
        std::vector<double> u = sim.u_noghost();
        std::vector<double> v = sim.v_noghost();

        writer.BeginStep();
        writer.Put<int>(var_step, &step, adios2::Mode::Sync);
        writer.Put<double>(var_u, u.data(), adios2::Mode::Sync);
        writer.Put<double>(var_v, v.data(), adios2::Mode::Sync);
        writer.EndStep();
    }
}

void Writer::close() { writer.Close(); }

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
