// __author__ = Juan Carlos De Abreu (jcabreur@gmail.com)
//
// These class define functors with 2 arguments.
//
// The functor allow have a pointer a function that can be changed when the
// programer wanted.
//
// The class receive three(3) parameter;
//   R : Return type of the function.
//   T1 : first parameter Type of the function.
//   T2 : second parameter Type of the function.

#ifndef FUNCTOR_FUNCTOR2_H__
#define FUNCTOR_FUNCTOR2_H__

#include "../../global.h"

BEGIN_TEG_NAMESPACE();

// Definition of the structure that manage a ponter to a function whith 2
// parameter.
template<typename R, typename T1, typename T2>
class Functor2 {
public:
  // Functor2 constructor.
  Functor2() { }

  // Functor2 destructor.
  virtual ~Functor2() { }

  // Methods.
  virtual R call(T1 t1, T2 t2) = 0; // Method to call the function that has the functor.
};

template<typename R, typename T1, typename T2>
class FunctorGlobal2 : public Functor2<R, T1, T2> {
private:
  // Attributes.
  R (*pFunc)(T1, T2); // Pointer to a function.

public:
  // FunctorGlobal2 constructor.
  FunctorGlobal2(R (*pFunc_)(T1, T2) = NULL);
  
  // FunctorGlobal2 destructor.
  ~FunctorGlobal2() { pFunc = NULL; }

  // Methods.
  R call(T1 t1, T2 t2); // Method to call the function that has the functor.
};

template<typename R, typename T1, typename T2, typename B>
class FunctorMember2 : public Functor2<R, T1, T2> {
private:
  // Attributes.
  R (B::*pFunc)(T1, T2); // Pointer to a function.
  B *pClass;             // Pointer to the base class.

public:
  // FunctorMember2 constructor.
  FunctorMember2(R (B::*pFunc_)(T1, T2) = NULL, B *pClass_ = NULL);
  
  // FunctorMember2 destructor.
  ~FunctorMember2() { pFunc = NULL; }

  // Methods.
  R call(T1 t1, T2 t2); // Method to call the function that has the functor.
};

template<typename R, typename T1, typename T2, typename B>
class FunctorMemberConst2 : public Functor2<R, T1, T2> {
private:
  // Attributes.
  R (B::*pFunc)(T1, T2) const; // Pointer to a function.
  B *pClass;                   // Pointer to the base class.

public:
  // FunctorMemberConst2 constructor.
  FunctorMemberConst2(R (B::*pFunc_)(T1, T2) const = NULL, B *pClass_ = NULL); 

  // FunctorMemberConst2 destructor.
  ~FunctorMemberConst2() { pFunc = NULL; }

  // Methods.
  R call(T1 t1, T2 t2); // Method to call the function that has the functor.
};

// FunctorGlobal2 constructor.
template<typename R, typename T1, typename T2>
inline FunctorGlobal2<R, T1, T2>::FunctorGlobal2(
    R (*pFunc_)(T1, T2)) : pFunc(pFunc_) {
  // Do anything ...
}

// FunctorMember2 constructor.
template<typename R, typename T1, typename T2, typename B>
inline FunctorMember2<R, T1, T2, B>::FunctorMember2(
    R (B::*pFunc_)(T1, T2), B *pClass_) : pFunc(pFunc_), pClass(pClass_) {
  // Do anything ...
}

// FunctorMemberConst2 constructor.
template<typename R, typename T1, typename T2, typename B>
inline FunctorMemberConst2<R, T1, T2, B>::FunctorMemberConst2(
    R (B::*pFunc_)(T1, T2) const, B *pClass_)
    : pFunc(pFunc_), pClass(pClass_) {
  // Do anything ...
}

// Method to call the function that has the functor.
template<typename R, typename T1, typename T2>
inline R FunctorGlobal2<R, T1, T2>::call(T1 t1, T2 t2) {
  // If I have a function then caller.
  return pFunc == NULL ? R() : (*pFunc)(t1, t2);
}

// Method to call the function that has the functor.
template<typename R, typename T1, typename T2, typename B>
inline R FunctorMember2<R, T1, T2, B>::call(T1 t1, T2 t2) {
  // If I have a function then caller.
  return pFunc == NULL ? R() : (pClass->*pFunc)(t1, t2);
}

// Method to call the function that has the functor.
template<typename R, typename T1, typename T2, typename B>
inline R FunctorMemberConst2<R, T1, T2, B>::call(T1 t1, T2 t2) {
  // If I have a function then caller.
  return pFunc == NULL ? R() : (pClass->*pFunc)(t1, t2);
}

// Function to create a Functor1 with a global function.
template<typename R, typename T1, typename T2>
Functor2<R, T1, T2>* newFunctor2(R (*pFunc)(T1, T2)) {
  return new FunctorGlobal2<R, T1, T2> (pFunc);
}

// Function to create a Functor1 with a member function.
template<typename R, typename T1, typename T2, typename B>
Functor2<R, T1, T2>* newFunctor2(R (B::*pFunc)(T1, T2), B *pClass = NULL) {
  return new FunctorMember2<R, T1, T2, B> (pFunc, pClass);
}

// Function to create a Functor1 with a member const function.
template<typename R, typename T1, typename T2, typename B>
Functor2<R, T1, T2>* newFunctor2(R (B::*pFunc)(T1, T2) const, B *pClass = NULL) {
  return new FunctorMemberConst2<R, T1, T2, B> (pFunc, pClass);
}

END_TEG_NAMESPACE();

#endif  // FUNCTOR_FUNCTOR2_H__
