#ifndef __JLANG__JPREDICATE__
#define __JLANG__JPREDICATE__

#include "JLang/JClass.hh"
#include "JLang/JComparison.hh"


/**
 * \author mdejong
 */

namespace JLANG {}
namespace JPP { using namespace JLANG; }

namespace JLANG {

  /**
   * Template definition of auxiliary class to select objects.
   * The first template argument refers to the data member or member method and
   * the second template argument to the comparator of the corresponding values.
   */
  template<class JTypename_t, class JComparator_t = JComparison::eq>
  class JPredicate;


  /**
   * Template specialisation of JPredicate for selection of objects via data member.
   */
  template<class T, class JResult_t, class JComparator_t>
  class JPredicate<JResult_t T::*, JComparator_t> {
  public:    

    typedef JResult_t                                   data_type;
    typedef typename JClass<data_type>::argument_type   argument_type;
    typedef JResult_t T::*data_member;                    //!< Type definition of data member
    typedef JComparator_t  comparator_type;               //!< Type definition of comparator
        
    
    /**
     * Constructor.
     *
     * \param  member       pointer to data member
     * \param  value        value
     * \param  comparator   comparator method
     */
    JPredicate(data_member            member,
	       argument_type          value,
	       const comparator_type& comparator = comparator_type()) :
      member    (member),
      value     (value),
      comparator(comparator)
    {}


    /**
     * Select objets.
     *
     * \param  object       object
     * \return              true if selected; else false
     */
    bool operator()(const T& object) const
    {
      return comparator(object.*member, value);
    }

  protected:
    data_member     member;
    data_type       value;
    comparator_type comparator;
  };


  /**
   * Template specialisation of JPredicate for selection of objects via member method.
   */
  template<class T, class JResult_t, class JComparator_t>
  class JPredicate<JResult_t (T::*)() const, JComparator_t> {
  public:    

    typedef JResult_t                                   data_type;
    typedef typename JClass<data_type>::argument_type   argument_type;
    typedef JResult_t (T::*member_method)() const;        //!< Type definition of member method
    typedef JComparator_t  comparator_type;               //!< Type definition of comparator
    
    
    /**
     * Constructor.
     *
     * \param  function     pointer to member method
     * \param  value        value
     * \param  comparator   comparator method
     */
    JPredicate(member_method          function,
	       argument_type          value,
	       const comparator_type& comparator = comparator_type()) :
      function  (function),
      value     (value),
      comparator(comparator)
    {}


    /**
     * Select objets.
     *
     * \param  object       object
     * \return              true if selected; else false
     */
    bool operator()(const T& object) const
    {
      return comparator((object.*function)(), value);
    }

  protected:
    member_method   function;
    data_type       value;
    comparator_type comparator;
  };


  /**
   * Helper method to create predicate for data member.
   *
   * \param  member       pointer to data member
   * \param  value        value
   * \return              predicate
   */
  template<class T, class JResult_t>
  JPredicate<JResult_t T::*, JComparison::eq> make_predicate(JResult_t T::*member,
							     const JResult_t value)
  {
    return JPredicate<JResult_t T::*, JComparison::eq>(member, value, JComparison::eq());
  }


  /**
   * Helper method to create predicate for data member.
   *
   * \param  member       pointer to data member
   * \param  value        value
   * \param  comparator   comparator between values of data members
   * \return              predicate
   */
  template<class T, class JResult_t, class JComparator_t>
  JPredicate<JResult_t T::*, JComparator_t> make_predicate(JResult_t T::*member,
							   const JResult_t value,
							   const JComparator_t& comparator)
  {
    return JPredicate<JResult_t T::*, JComparator_t>(member, value, comparator);
  }


  /**
   * Helper method to create predicate for return values of member method.
   *
   * \param  function     pointer to member method
   * \param  value        value
   * \return              predicate
   */
  template<class T, class JResult_t>
  JPredicate<JResult_t (T::*)() const, JComparison::eq> make_predicate(JResult_t (T::*function)() const,
								       const JResult_t value)
  {
    return JPredicate<JResult_t (T::*)() const, JComparison::eq>(function, value, JComparison::eq());
  }


  /**
   * Helper method to create predicate for return values of member method.
   *
   * \param  function     pointer to member method
   * \param  value        value
   * \return              predicate
   */
  template<class T, class JResult_t>
  JPredicate<const JResult_t& (T::*)() const, JComparison::eq> make_predicate(const JResult_t& (T::*function)() const,
									      const JResult_t& value)
  {
    return JPredicate<const JResult_t& (T::*)() const, JComparison::eq>(function, value, JComparison::eq());
  }


  /**
   * Helper method to create predicate for return values of member method.
   *
   * \param  function     pointer to member method
   * \param  value        value
   * \param  comparator   comparator between return values
   * \return              predicate
   */
  template<class T, class JResult_t, class JComparator_t>
  JPredicate<JResult_t (T::*)() const, JComparator_t> make_predicate(JResult_t (T::*function)() const,
								     const JResult_t value,
								     const JComparator_t& comparator)
  {
    return JPredicate<JResult_t (T::*)() const, JComparator_t>(function, value, comparator);
  }


  /**
   * Helper method to create predicate for return values of member method.
   *
   * \param  function     pointer to member method
   * \param  value        value
   * \param  comparator   comparator between return values
   * \return              predicate
   */
  template<class T, class JResult_t, class JComparator_t>
  JPredicate<const JResult_t& (T::*)() const, JComparator_t> make_predicate(const JResult_t& (T::*function)() const,
									    const JResult_t& value,
									    const JComparator_t& comparator)
  {
    return JPredicate<const JResult_t& (T::*)() const, JComparator_t>(function, value, comparator);
  }
}

#endif