#ifndef __JOSCPROB__JOSCPROBINTERFACE__
#define __JOSCPROB__JOSCPROBINTERFACE__

#include <memory>

#include "JLang/JClonable.hh"
#include "JLang/JException.hh"

#include "JOscProb/JOscChannel.hh"
#include "JOscProb/JBaselineComputer.hh"
#include "JOscProb/JOscParametersHelper.hh"


/**
 * \author bjung, mdejong
 */

namespace JOSCPROB {}
namespace JPP { using namespace JOSCPROB; }

namespace JOSCPROB {

  using JLANG::JClonable;


  /**
   * Low-level interface for oscillation probability calculators.
   */
  class JOscProbInterface :
    public JBaselineComputer,
    public JOscParametersHelper<double&>,
    public JClonable<JOscProbInterface>
  {
  public:

    typedef JOscParametersHelper<double&>                        JOscParametersHelper_t;

    typedef JOscParametersInterface<double>                      JOscParameters_t;
    typedef JOscParametersInterface<double&>                     JOscParameterReferences_t;
    
    typedef typename JOscParameters_t::JOscParameter_t           JOscParameter_t;    
    typedef typename JOscParameters_t::JParameter_t              JParameter_t;
    

    /**
     * Default constructor.
     */
    JOscProbInterface()
    {}


    /**
     * Constructor.
     *
     * \param  parameters         oscillation parameters
     */
    JOscProbInterface(const JOscParameterReferences_t& parameters) :
      JOscParametersHelper_t(parameters)
    {}


    /**
     * Constructor.
     *
     * \param  parameters         oscillation parameters
     * \param  name               parameter name
     * \param  value              parameter value
     * \param  args               remaining pairs of parameter names and values
     */
    template<class ...Args>
    JOscProbInterface(const JOscParameterReferences_t& parameters,
		      const std::string&               name,
		      double&                          value,
		      const Args&                   ...args) :
      JOscParametersHelper_t(parameters, name, value, args...)
    {}


    /**
     * Virtual destructor.
     */
    virtual ~JOscProbInterface()
    {}


    /**
     * Get oscillation probability for a given oscillation channel.
     *
     * \param  channel            oscillation channel
     * \param  E                  neutrino energy [GeV]
     * \param  costh              cosine zenith angle
     * \return                    oscillation probability
     */
    virtual double getP(const JOscChannel&    channel,
			const double          E,
			const double          costh) const = 0;


    /**
     * Get oscillation probability for a given set of oscillation parameters\n
     * and a given oscillation channel.
     *
     * \param  channel            oscillation channel
     * \param  parameters         oscillation parameters
     * \param  E                  neutrino energy [GeV]
     * \param  costh              cosine zenith angle
     * \return                    oscillation probability
     */
    double getP(const JOscParameters_t& parameters,
		const JOscChannel&      channel,
		const double            E,
		const double            costh) const
    {
      this->set(parameters);

      return getP(channel, E, costh);      
    }


    /**
     * Get oscillation probability for a given oscillation parameter\n
     * and a given oscillation channel.
     *
     * \param  name               parameter name
     * \param  value              parameter value
     * \param  channel            oscillation channel
     * \param  E                  neutrino energy [GeV]
     * \param  costh              cosine zenith angle
     * \return                    oscillation probability
     */
    double getP(const std::string& name,
		const double       value,
		const JOscChannel& channel,
		const double       E,
		const double       costh) const
    {
      this->set(name, value);

      return getP(channel, E, costh);
    }
    

    /**
     * Get oscillation probability for a given set of oscillation parameters\n
     * and a given oscillation channel.
     *
     * \param  name            parameter name
     * \param  value           parameter value
     * \param  args            remaining arguments
     */
    template<class ...Args>
    double getP(const std::string& name,
		const double       value,
		const Args&     ...args) const
    {
      this->set(name, value);
      
      return getP(args...);
    }

    
    /**
     * Get oscillation probability for a given oscillation channel.
     *
     * \param  channel            oscillation channel
     * \param  E                  neutrino energy [GeV]
     * \param  costh              cosine zenith angle
     * \return                    oscillation probability
     */
    double operator()(const JOscChannel&    channel,
		      const double          E,
		      const double          costh) const
    {
      return getP(channel, E, costh);
    }
    
    
    /**
     * Get oscillation probability for a given set of oscillation parameters\n
     * and a given oscillation channel.
     *
     * \param  channel            oscillation channel
     * \param  parameters         oscillation parameters
     * \param  E                  neutrino energy [GeV]
     * \param  costh              cosine zenith angle
     * \return                    oscillation probability
     */
    double operator()(const JOscParameters_t& parameters,
		      const JOscChannel&      channel,
		      const double            E,
		      const double            costh) const
    {
      return getP(parameters, channel, E, costh);
    }


    /**
     * Get oscillation probability for a given oscillation parameter\n
     * and a given oscillation channel.
     *
     * \param  name               parameter name
     * \param  value              parameter value
     * \param  channel            oscillation channel
     * \param  E                  neutrino energy [GeV]
     * \param  costh              cosine zenith angle
     * \return                    oscillation probability
     */
    double operator()(const std::string& name,
		      const double       value,
		      const JOscChannel& channel,
		      const double       E,
		      const double       costh) const
    {
      return getP(name, value, channel, E, costh);
    }


    /**
     * Get oscillation probability for a given set of oscillation parameters\n
     * and a given oscillation channel.
     *
     * \param  name            parameter name
     * \param  value           parameter value
     * \param  args            remaining arguments
     */
    template<class ...Args>    
    double operator()(const std::string& name,
		      const double       value,
		      const Args&     ...args) const
    {
      return getP(name, value, args...);
    }
  };
}

#endif