#ifndef __JFIT__JMODEL__
#define __JFIT__JMODEL__

#include "JFit/JLine1Z.hh"
#include "JFit/JEnergy.hh"
#include "JFit/JPoint4D.hh"
#include "JFit/JNPE.hh"
#include "JFit/JTimeRange.hh"
#include "JTools/JRange.hh"
#include "JPhysics/JConstants.hh"

/**
 * \author mdejong
 */

namespace JFIT {}
namespace JPP { using namespace JFIT; }

namespace JFIT {

  typedef JTOOLS::JRange<double> JZRange;
 
  /**
   * Auxiliary class to match data points with given model.
   */
  template<class JModel_t>
  struct JModel;


  /**
   * Template specialisation of class JModel to match hit with muon trajectory along z-axis.
   */
  template<>
  struct JModel<JLine1Z> :
    public JLine1Z
  {
    /**
     * Constructor.
     *
     * \param  tz             muon along z-axis
     * \param  Rmax_m         maximal distance of approach [m]
     * \param  compare        time window [ns]
     * \param  z_m            z-range of muon [m]
     */
    JModel(const JLine1Z&    tz,
	   const double      Rmax_m,
	   const JTimeRange& compare,
	   const JZRange&    z_m = JZRange()) :
      JLine1Z(tz)
    {
      this->Rmax_m  = Rmax_m;
      this->compare = compare;
      this->z_m     = z_m;
    }


    /**
     * Test compatibility of given hit with track.
     *
     * \param  hit            hit
     * \return                true if compatible; else false
     */
    template<class JHit_t>
    bool operator()(const JHit_t& hit) const
    {
      using namespace std;
      using namespace JPP;

      if (this->getDistance(hit) <= Rmax_m) {
	if (hit.getZ()                                         - this->getZ()  >=  z_m.getLowerLimit()   &&
	    hit.getZ() - this->getDistance(hit)/getTanThetaC() - this->getZ()  <=  z_m.getUpperLimit()) {
	  return compare(hit.getT() - this->getT(hit));
	} else {
	  return false;
	}
      }  
      return false;
    }
   
  protected:
    double     Rmax_m;
    JTimeRange compare;
    JZRange    z_m;
  };


  /**
   * Template specialisation of class JModel to match hit with muon energy.
   */
  template<>
  struct JModel<JEnergy> :
    public JEnergy
  {
    /**
     * Constructor.
     *
     * \param  X              x-value [log(E)]
     */
    JModel(const JEnergy& X) :
      JEnergy(X)
    {}


    /**
     * Test compatibility of given light yields with muon energy.
     *
     * \param  npe            npe
     * \return                true if total light yield due to muon is larger than that due to random background; else false
     */
    bool operator()(const JNPE& npe) const
    {
      return npe.getYA() + this->getE() * npe.getYB()  >  npe.getY0();
    }
  };

  
  /**
   * Template specialisation of class JModel to match hit with bright point.
   */
  template<>
  struct JModel<JPoint4D> :
    public JPoint4D
  {
    /**
     * Constructor.
     *
     * \param  point          bright point
     * \param  Dmax_m         maximal distance of approach [m]
     * \param  compare        time window [ns]
     */
    JModel(const JPoint4D&   point,
	   const double      Dmax_m,
	   const JTimeRange& compare) :
      JPoint4D(point)
    {
      this->Dmax_m  = Dmax_m;
      this->compare = compare;
    }

    
    /**
     * Test compatibility of given hit with bright point.
     *
     * \param   hit           hit
     * \return                true if compatible; else false
     */
    template<class JHit_t>
    bool operator()(const JHit_t& hit) const
    {
      if (this->getDistance(hit) <= Dmax_m) {
        return compare(hit.getT() - this->getT(hit));
      }

      return false;
    }

  protected:
    double     Dmax_m;
    JTimeRange compare;
  };
}

#endif