//
// Created by Eric Irrgang on 3/24/18.
//

#include "testingconfiguration.h"

#include <iostream>
#include <vector>

#include "ensemblepotential.h"

#include <gtest/gtest.h>

namespace {

using ::gmx::Vector;

std::ostream& operator<<(std::ostream& stream, const gmx::Vector& vec)
{
    stream << "(" << vec[0] << "," << vec[1] << "," << vec[2] << ")";
    return stream;
}

const auto filename = plugin::testing::sample_tprfilename;

TEST(EnsembleBoundingPotentialPlugin, ForceCalc)
{
    const Vector zerovec {0, 0, 0};
    // define some unit vectors
    const Vector e1{real(1), real(0), real(0)};
    const Vector e2{real(0), real(1), real(0)};
    const Vector e3{real(0), real(0), real(1)};

    const real R0{1.0};
    const real k{1.0};

    // store temporary values long enough for inspection
    Vector force{};

    // Get a dummy EnsembleResources. We aren't testing that here.
    auto dummyFunc = [](const plugin::Matrix<double>&, plugin::Matrix<double>*){
        return;};
    auto resource = std::make_shared<plugin::Resources>(dummyFunc);

    // Define a reference distribution with a triangular peak at the 1.0 bin.
    const std::vector<double>
        experimental{{0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}};


    plugin::EnsemblePotential restraint{10, // nbins
                                       1.0, // binWidth
                                       5.0, // minDist
                                       5.0, // maxDist
                                       experimental, // experimental reference histogram
                                       1, // nSamples
                                       0.001, // samplePeriod
                                       1, // nWindows
                                       100., // k
                                       1.0 // sigma
    };

    auto calculateForce =
        [&restraint](const Vector& a, const Vector& b, double t)
        {
            return restraint.calculate(a,b,t).force;
        };

    // Atoms should be driven towards each other when above maxDist and and away under minDist.
    force = calculateForce(e1,
                           static_cast<real>(3)*e1,
                           0.001);
    ASSERT_LT(force[0], 0.) << " where force is (" << force[0] << ", " << force[1] << ", " << force[2] << ")\n";
    force = calculateForce(e1,
                           static_cast<real>(7)*e1,
                           0.001);
    ASSERT_GT(force[0], 0.) << " where force is (" << force[0] << ", " << force[1] << ", " << force[2] << ")\n";
}

} // end anonymous namespace
