// __author__ = Juan Carlos De Abreu (jcabreur@gmail.com)

//
// These class define functors with 3 arguments.
//
// The functor allow have a pointer a function that can be changed when the
// programer wanted.
//
// The class receive four(4) parameter;
//   R : Return type of the function.
//   T1 : first parameter Type of the function.
//   T2 : second parameter Type of the function.
//   T3 : third parameter Type of the function.

#ifndef FUNCTOR_FUNCTOR3_H__
#define FUNCTOR_FUNCTOR3_H__

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

BEGIN_TEG_NAMESPACE();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// FunctorMemberConst3 constructor.
template<typename R, typename T1, typename T2, typename T3, typename B>
inline FunctorMemberConst3<R, T1, T2, T3, B>::FunctorMemberConst3(
    R (B::*pFunc_)(T1, T2, T3) 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, typename T3>
inline R FunctorGlobal3<R, T1, T2, T3>::call(T1 t1, T2 t2, T3 t3) {
  // If I have a function then caller.
  return pFunc == NULL ? R() : (*pFunc)(t1, t2, t3);
}

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

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

// Function to create a Functor1 with a global function.
template<typename R, typename T1, typename T2, typename T3>
Functor3<R, T1, T2, T3>* newFunctor3(R (*pFunc)(T1, T2, T3)) {
  return new FunctorGlobal3<R, T1, T2, T3> (pFunc);
}

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

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

END_TEG_NAMESPACE();

#endif  // FUNCTOR_FUNCTOR3_H__
