// __author__ = Juan Carlos De Abreu (jcabreur@gmail.com)
//
// These class define functors with 4 arguments.
//
// The functor allow have a pointer a function that can be changed when the
// programer wanted.
//
// The class receive five(5) 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.
//   T4 : fourth parameter Type of the function.

#ifndef FUNCTOR_FUNCTOR4_H__
#define FUNCTOR_FUNCTOR4_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, typename T4>
class Functor4 {
public:
  // Functor4 constructor.
  Functor4() { }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

END_TEG_NAMESPACE();

#endif  // FUNCTOR_FUNCTOR4_H__
