#include "MD_method.h"

#include "Position_leapfrog.h"
#include "Velocity_leapfrog.h"
#include "Position_verlet.h"
#include "Velocity_verlet.h"
#include "Verlet.h"
#include "MTS_MD.h"
#include "rRESPA_4.h"
#include "Shuffled.h"
#include "MTS_VV.h"
#include "MTS_VP.h"
#include "OPVL.h"
#include "OVVL.h"
#include "VEFRL.h"
#include "PEFRL.h"
#include "Classic_Euler.h"
#include "Position_Euler.h"
#include "Velocity_Euler.h"
#include "HMC.h"
#include "ATHMC.h"

#include "Create.h"

namespace MM
{
Position_leapfrog * prototype_of_Position_leapfrog  = new Position_leapfrog (Prototype());
Velocity_leapfrog * prototype_of_Velocity_leapfrog  = new Velocity_leapfrog (Prototype());
Position_verlet *   prototype_of_Position_verlet    = new Position_verlet   (Prototype());
Velocity_verlet *   prototype_of_Velocity_verlet    = new Velocity_verlet   (Prototype());
Verlet *            prototype_of_Verlet             = new Verlet            (Prototype());
MTS_MD *            prototype_of_MTS_MD             = new MTS_MD            (Prototype());
MTS_VV *            prototype_of_MTS_VV             = new MTS_VV            (Prototype());
rRESPA_4 *          prototype_of_rRESPA_4           = new rRESPA_4          (Prototype());
Shuffled *          prototype_of_Shuffled           = new Shuffled          (Prototype());
MTS_VP *            prototype_of_MTS_VP             = new MTS_VP            (Prototype());
OPVL *              prototype_of_OPVL               = new OPVL              (Prototype());
OVVL *              prototype_of_OVVL               = new OVVL              (Prototype());
VEFRL *             prototype_of_VEFRL              = new VEFRL             (Prototype());
PEFRL *             prototype_of_PEFRL              = new PEFRL             (Prototype());
Classic_Euler *     prototype_of_Classic_Euler      = new Classic_Euler     (Prototype());
Position_Euler *    prototype_of_Position_Euler     = new Position_Euler    (Prototype());
Velocity_Euler *    prototype_of_Velocity_Euler     = new Velocity_Euler    (Prototype());
HMC *               prototype_of_HMC                = new HMC               (Prototype());
ATHMC *             prototype_of_ATHMC              = new ATHMC             (Prototype());

// ======================================================
MD_method * MD_method::
create (Text const & method)
{
    for (int i=0;  i<prototype_count();  ++i)
        if (method == prototype (i). class_name ())
            return prototype (i).clone ();

    FLAW (method + " - no such algorithm.");
}

// fix to template ======================================
namespace
{
    Array_of_own <MD_method> & prototype_list ()
    {
        static Array_of_own <MD_method>   instance;
        return instance;
    }
}

int MD_method::
prototype_count ()
{
    return prototype_list().size();
}

MD_method & MD_method::
prototype (int n)
{
    return prototype_list()[n];
}

void MD_method::
adopt_prototype (MD_method * method)
{
    prototype_list().push_back (method);
}
//==================================================================

MD_method::
~MD_method ()
{
    simulation_ = 0;
    step_N_     = 0;
}

void MD_method::
start ()
{
    step_N_ = 0;
    simulation().algorithm().description
        (Text("start  ") + simulation ().title());
}

void MD_method::
finish ()
{
    simulation().algorithm().description
        (Text("finish ") + simulation ().title());
}


void MD_method::zero_virial ()
{
    INVARIANT;
    simulation_->interface_for(this).zero_virial ();
}

void MD_method::
list (MD_subject::Hint hint, double skin)
{
    INVARIANT;
    simulation_->interface_for(this).list (hint, skin);
}

void MD_method::
list (MD_subject::Hint hint, double R, double skin)
{
    INVARIANT;
    simulation_->interface_for(this).list (hint, R, skin);
}

double MD_method::
degrees_of_freedom () const
{
    INVARIANT;
    return simulation_->interface_for(this).degrees_of_freedom ();
}

double MD_method::
H () const
{
    INVARIANT;
    return simulation_->interface_for(this).H ();
}

double MD_method::
K () const
{
    INVARIANT;
    return simulation_->interface_for(this).K ();
}

double MD_method::
U () const
{
    INVARIANT;
    return simulation_->interface_for(this).U ();
}

void MD_method::
F (MD_subject::Hint hint)
{
    INVARIANT;
    simulation_->interface_for(this).F (hint);
}
void MD_method::
F (MD_subject::Hint hint, double R, double Switching)
{
    INVARIANT;
    simulation_->interface_for(this).F (hint, R, Switching);
}
void MD_method::
clear_moment (MD_subject::Hint hint)
{
    INVARIANT;
    simulation_->interface_for(this).clear_moment (hint);
}
void MD_method::
kick (double h, MD_subject::Hint hint)
{
    INVARIANT;
    simulation_->interface_for(this).kick (h, hint);
}
void MD_method::
kick (int substeps, double h, MD_subject::Hint hint)
{
    INVARIANT;
    simulation_->interface_for(this).kick (substeps, h, hint);
}

void MD_method::
drift(double h)
{
    INVARIANT;
    simulation_->interface_for(this).drift(h);
}

void MD_method::
keep_configuration ()
{
    INVARIANT;
    simulation_->interface_for(this).keep_configuration();
}

void MD_method::
restore_configuration ()
{
    INVARIANT;
    simulation_->interface_for(this).restore_configuration();
}

}//MM

