#ifndef __JLANG__JMULTIEQUALS__
#define __JLANG__JMULTIEQUALS__

#include "JLang/JNullType.hh"
#include "JLang/JTypeList.hh"
#include "JLang/JType.hh"


/**
 * \author mdejong
 */

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

namespace JLANG {


  /**
   * Template definition of auxiliary base class for data structures
   * composed of multiple base classes with equality evaluations capabilities.
   *
   * The data type <tt>JType_t</tt> should have the corresponding operator:
   * <pre>
   *       bool operator==(const JType_t& first, const JType_t& second);
   * </pre>
   * This class uses in-class friend operators (see Barton-Nackman trick).
   *
   * This class implements the operators <tt> == != </tt>.
   */
  template<class JClass_t, class JType_t>
  struct JMultiEquals {
    /**
     * Equal operator.
     *
     * \param  first        first  object
     * \param  second       second object
     * \return              true if two objects are equal; else false
     */
    friend bool operator==(const JClass_t& first,
			   const JClass_t& second)
    {
      return static_cast<const JType_t&>(first) == static_cast<const JType_t&>(second);
    }


    /**
     * Not equal operator.
     *
     * \param  first        first  object
     * \param  second       second object
     * \return              true if two objects are not equal; else false
     */
    friend bool operator!=(const JClass_t& first,
			   const JClass_t& second)
    {
      return static_cast<const JType_t&>(first) != static_cast<const JType_t&>(second);
    }
  };


  /**
   * Template specialisation of auxiliary base class for data structures
   * composed of multiple base classes with equality evaluations capabilities.
   *
   * Each data type <tt>T</tt> in the type list should have the corresponding operator:
   * <pre>
   *       bool operator==(const T& first, const T& second);
   * </pre>
   * This class uses in-class friend operators (see Barton-Nackman trick).
   *
   * This class implements the operators <tt> == != </tt>.
   */
  template<class JClass_t, class head_type, class tail_type>
  struct JMultiEquals<JClass_t, JTypeList<head_type, tail_type> > {
  protected:

    typedef JTypeList<head_type, tail_type>   JTypelist_t;


    /**
     * Equals method for composite data types.
     *
     * \param  first        first  object
     * \param  second       second object
     * \param  type         type
     * \return              true if two objects are equal; else false
     */
    template<class JHead_t, class JTail_t>
    static inline bool eq(const JClass_t& first,
			  const JClass_t& second,
			  const JType<JTypeList<JHead_t, JTail_t> >& type)
    {
      return (static_cast<const JHead_t&>(first)  ==
	      static_cast<const JHead_t&>(second) &&
	      eq(first, second, JType<JTail_t>()));
    }
    

    /**
     * Equals method for composite data types.
     *
     * \param  first        first  object
     * \param  second       second object
     * \param  type         type
     * \return              true if two objects are equal; else false
     */
    template<class JHead_t>
    static inline bool eq(const JClass_t& first,
			  const JClass_t& second,
			  const JType<JTypeList<JHead_t, JNullType> >& type)
    {
      return (static_cast<const JHead_t&>(first) ==
	      static_cast<const JHead_t&>(second));
    }

  public:
    /**
     * Equal operator.
     *
     * \param  first        first  object
     * \param  second       second object
     * \return              true if two objects are equal; else false
     */
    friend bool operator==(const JClass_t& first,
			   const JClass_t& second)
    {
      return eq(first, second, JType<JTypelist_t>());
    }


    /**
     * Not equal operator.
     *
     * \param  first        first  object
     * \param  second       second object
     * \return              true if two objects are not equal; else false
     */
    friend bool operator!=(const JClass_t& first,
			   const JClass_t& second)
    {
      return !eq(first, second, JType<JTypelist_t>());
    }
  };
}

#endif