#ifndef ENERGY_TERMS_H
#define ENERGY_TERMS_H

#ifndef OWN_H
#include "Own.h"
#endif

#ifndef TEXT_H
#include "Text.h"
#endif

#ifndef TEXT_OUTPUT_H
#include "Text_output.h"
#endif

namespace MM
{

template <typename real>
struct Energy_terms
{
    Own <real>              bond;
    Own <real>              angle;
    Own <real>              torsion;
    Own <real>              improper_torsion;

    //Own <real>              intermolecular_potential;

    Own <real>              nonbonded;
    Own <real>              Van_der_Waals;
    Own <real>              electrostatic;

    Own <real>              external_fields;

    real                    potential_energy () const
    {
        return intramolecular_potential() + intermolecular_potential();
    }

    real                    intramolecular_potential () const
    {
        real result = 0;

        if (bond.            is_valid())    result += bond ();
        if (angle.           is_valid())    result += angle();
        if (torsion.         is_valid())    result += torsion();
        if (improper_torsion.is_valid())    result += improper_torsion();

        return result;
    }

    real                    intermolecular_potential () const
    {
        real result = 0;

        if (nonbonded.       is_valid()) return nonbonded();
        else
        {
            if (Van_der_Waals.   is_valid()) result += Van_der_Waals();
            if (electrostatic.   is_valid()) result += electrostatic();
            if (external_fields. is_valid()) result += external_fields();
        }

        return result;
    }

    Energy_terms &          operator = (Energy_terms const & e)
    {
        if (bond.            is_valid()) bond ()           = e.bond();
        if (angle.           is_valid()) angle()           = e.angle();
        if (torsion.         is_valid()) torsion()         = e.torsion();
        if (improper_torsion.is_valid()) improper_torsion()= e.improper_torsion();
        if (nonbonded.       is_valid()) nonbonded()       = e.nonbonded();
        if (Van_der_Waals.   is_valid()) Van_der_Waals()   = e.Van_der_Waals();
        if (electrostatic.   is_valid()) electrostatic()   = e.electrostatic();
        if (external_fields. is_valid()) external_fields() = e.external_fields();
	return *this;
    }

    Energy_terms &          operator += (Energy_terms const & e)
    {
        if (bond.            is_valid()) bond ()           += e.bond();
        if (angle.           is_valid()) angle()           += e.angle();
        if (torsion.         is_valid()) torsion()         += e.torsion();
        if (improper_torsion.is_valid()) improper_torsion()+= e.improper_torsion();
        if (nonbonded.       is_valid()) nonbonded()       += e.nonbonded();
        if (Van_der_Waals.   is_valid()) Van_der_Waals()   += e.Van_der_Waals();
        if (electrostatic.   is_valid()) electrostatic()   += e.electrostatic();
        if (external_fields. is_valid()) external_fields() += e.external_fields();
	return *this;
    }

    void                    set_to_zero ()
    {
        if (bond.            is_valid())    bond.            adopt (new real);
        if (angle.           is_valid())    angle.           adopt (new real);
        if (torsion.         is_valid())    torsion.         adopt (new real);
        if (improper_torsion.is_valid())    improper_torsion.adopt (new real);
        if (nonbonded.       is_valid())    nonbonded.       adopt (new real);
        if (Van_der_Waals.   is_valid())    Van_der_Waals.   adopt (new real);
        if (electrostatic.   is_valid())    electrostatic.   adopt (new real);
        if (external_fields. is_valid())    external_fields. adopt (new real);
    }

    Energy_terms &          write (Text_output& out) const
    {
        if (bond.is_valid())
        {
            out.text("bond             = ");
            out.line (Text(bond(),            15));
        }
        if (angle.is_valid())
        {
            out.text("angle            = ");
            out.line (Text(angle(),           15));
        }
        if (torsion.is_valid())
        {
            out.text("torsion          = ");
            out.line (Text(torsion(),         15));
        }
        if (improper_torsion.is_valid())
        {
            out.text("improper torsion = ");
            out.line (Text(improper_torsion(),15));
        }
        if (nonbonded.is_valid())
        {
            out.text("nonbonded        = ");
            out.line (Text(nonbonded(),       15));
        }
        if (Van_der_Waals.is_valid())
        {
            out.text("Van der Waals    = ");
            out.line (Text(Van_der_Waals(),   15));
        }
        if (electrostatic.is_valid())
        {
            out.text("electrostatic    = ");
            out.line (Text(electrostatic(),   15));
        }
        if (external_fields.is_valid())
        {
            out.text("external fields  = ");
            out.line (Text(external_fields(), 15));
        }
        return *this;
    }
};

}//MM

#endif //ENERGY_TERMS_H
