// Copyright (c) 2009-2016 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.


// Maintainer: joaander

#include "SystemDefinition.h"
#include "Profiler.h"

#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <string>
#include <vector>

#ifndef __COMPUTE_H__
#define __COMPUTE_H__

/*! \file Compute.h
    \brief Declares a base class for all computes
*/

#ifdef NVCC
#error This header cannot be compiled by nvcc
#endif

/*! \ingroup hoomd_lib
    @{
*/

/*! \defgroup computes Computes
    \brief All classes that implement the Compute concept.
    \details See \ref page_dev_info for more information
*/

/*! @}
*/

//! Performs computations on ParticleData structures
/*! The Compute is an abstract concept that performs some kind of computation on the
    particles in a ParticleData structure. This computation is to be done by reading
    the particle data only, no writing. Computes will be used to generate neighbor lists,
    calculate forces, and calculate temperatures, just to name a few.

    For performance and simplicity, each compute is associated with a ParticleData
    on construction. ParticleData pointers are managed with reference counted boost::shared_ptr.
    Since each ParticleData cannot change size, this allows the Compute to preallocate
    any data structures that it may need.

    Computes may be referenced more than once and may reference other computes. To prevent
    uneeded data from being calculated, the time step will be passed into the compute
    method so that it can skip caculations if they have already been done this timestep.
    For convenience, the base class will provide a shouldCompute() method that implements
    this behaviour. Derived classes can override if more complicated behavior is needed.

    See \ref page_dev_info for more information
    \ingroup computes
*/
class Compute : boost::noncopyable
    {
    public:
        //! Constructs the compute and associates it with the ParticleData
        Compute(boost::shared_ptr<SystemDefinition> sysdef);
        virtual ~Compute() {};

        //! Abstract method that performs the computation
        /*! \param timestep Current time step
            Derived classes will implement this method to calculate their results
        */
        virtual void compute(unsigned int timestep) = 0;

        //! Abstract method that performs a benchmark
        virtual double benchmark(unsigned int num_iters);

        //! Print some basic stats to stdout
        /*! Derived classes can optionally implement this function. A System will
            call all of the Compute's printStats functions at the end of a run
            so the user can see useful information
        */
        virtual void printStats()
            {
            }

        //! Reset stat counters
        /*! If derived classes implement printStats, they should also implement resetStats() to clear any running
            counters printed by printStats. System will reset the stats before any run() so that stats printed
            at the end of the run only apply to that run() alone.
        */
        virtual void resetStats()
            {
            }

        //! Sets the profiler for the compute to use
        void setProfiler(boost::shared_ptr<Profiler> prof);

        //! Set autotuner parameters
        /*! \param enable Enable/disable autotuning
            \param period period (approximate) in time steps when returning occurs

            Derived classes should override this to set the parameters of their autotuners.
        */
        virtual void setAutotunerParams(bool enable, unsigned int period)
            {
            }

        //! Returns a list of log quantities this compute calculates
        /*! The base class implementation just returns an empty vector. Derived classes should override
            this behavior and return a list of quantities that they log.

            See Logger for more information on what this is about.
        */
        virtual std::vector< std::string > getProvidedLogQuantities()
            {
            return std::vector< std::string >();
            }

        //! Calculates the requested log value and returns it
        /*! \param quantity Name of the log quantity to get
            \param timestep Current time step of the simulation

            The base class just returns 0. Derived classes should override this behavior and return
            the calculated value for the given quantity. Only quantities listed in
            the return value getProvidedLogQuantities() will be requested from
            getLogValue().

            See Logger for more information on what this is about.
        */
        virtual Scalar getLogValue(const std::string& quantity, unsigned int timestep)
            {
            return Scalar(0.0);
            }

        //! Force recalculation of compute
        /*! If this function is called, recalculation of the compute will be forced (even if had
         *  been calculated earlier in this timestep)
         * \param timestep current timestep
         */
        virtual void forceCompute(unsigned int timestep);

#ifdef ENABLE_MPI
        //! Set communicator this Compute is to use
        /*! \param comm The communicator
         */
        virtual void setCommunicator(boost::shared_ptr<Communicator> comm)
            {
            m_comm = comm;
            }
#endif

    protected:
        const boost::shared_ptr<SystemDefinition> m_sysdef; //!< The system definition this compute is associated with
        const boost::shared_ptr<ParticleData> m_pdata;      //!< The particle data this compute is associated with
        boost::shared_ptr<Profiler> m_prof;                 //!< The profiler this compute is to use
        boost::shared_ptr<const ExecutionConfiguration> exec_conf; //!< Stored shared ptr to the execution configuration
#ifdef ENABLE_MPI
        boost::shared_ptr<Communicator> m_comm;             //!< The communicator this compute is to use
#endif
        boost::shared_ptr<const ExecutionConfiguration> m_exec_conf; //!< Stored shared ptr to the execution configuration
        bool m_force_compute;           //!< true if calculation is enforced

        //! Simple method for testing if the computation should be run or not
        virtual bool shouldCompute(unsigned int timestep);

    private:
        unsigned int m_last_computed;   //!< Stores the last timestep compute was called
        bool m_first_compute;           //!< true if compute has not yet been called

        //! The python export needs to be a friend to export shouldCompute()
        friend void export_Compute();
    };

//! Exports the Compute class to python
void export_Compute();

#endif
