#ifndef FUNCTOR_HPP
#define FUNCTOR_HPP

#include <iostream>
#include <vector>

#include "simplefile.hpp"
#include "entry.hpp"
#include "dbaudiofile.hpp"

// overlay
#include "image.hpp"

#include "imdb.hpp"

#include "input.hpp"
#include "renderer.hpp"

#include "gettext.hpp"

template <typename TClass, typename element>
class TSpecificFunctor
{
 private:
  void (TClass::*fpt)(const std::vector<element>&);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  
 public:

  // constructor - takes pointer to an object and pointer to a member and stores
  // them in two private variables
  TSpecificFunctor(TClass* _pt2Object, void(TClass::*_fpt)(const std::vector<element>&))
  { pt2Object = _pt2Object;  fpt=_fpt; };

  void call(const std::vector<element>& list)
  { (*pt2Object.*fpt)(list);};             // execute member function
};

template <typename TClass, typename element>
class TSpecificFunctorNonconst
{
 private:
  void (TClass::*fpt)(std::vector<element>&);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  
 public:

  // constructor - takes pointer to an object and pointer to a member and stores
  // them in two private variables
  TSpecificFunctorNonconst(TClass* _pt2Object, void(TClass::*_fpt)(std::vector<element>&))
  { pt2Object = _pt2Object;  fpt=_fpt; };

  void call(std::vector<element>& list)
  { (*pt2Object.*fpt)(list);};             // execute member function
};

// print_range

template <typename TClass>
class TPRMovieFunctor
{
 private:
  void (TClass::*fpt)(const Entry& r, const Entry& position, int y);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  
 public:
  
  TPRMovieFunctor(TClass* _pt2Object, void(TClass::*_fpt)(const Entry&, const Entry&, int))
  { pt2Object = _pt2Object; fpt=_fpt; };

  void call(const Entry& r, const Entry& position, int y)
  { (*pt2Object.*fpt)(r, position, y);};             // execute member function
};

template <typename TClass>
class TPRAudioFunctor
{
 private:
  void (TClass::*fpt)(const Simplefile& r, const Simplefile& position, 
		      int y);   // pointer to member function
  TClass* pt2Object;            // pointer to object
  
 public:
  
  TPRAudioFunctor(TClass* _pt2Object, void(TClass::*_fpt)(const Simplefile&, const Simplefile&, 
							  int))
  { pt2Object = _pt2Object; fpt=_fpt; };

  void call(const Simplefile& r, const Simplefile& position, int y)
  { (*pt2Object.*fpt)(r, position, y);};             // execute member function
};

template <typename TClass>
class TPRGAudioFunctor
{
 private:
  void (TClass::*fpt)(Dbaudiofile& r, const Dbaudiofile& position, 
		      int y, bool);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  bool picture_dir;
  
 public:
  
  TPRGAudioFunctor(TClass* _pt2Object, void(TClass::*_fpt)(Dbaudiofile&, const Dbaudiofile&, 
							   int, bool), bool _pic_dir)
  { pt2Object = _pt2Object; fpt=_fpt; picture_dir = _pic_dir; };

  void call(Dbaudiofile& r, const Dbaudiofile& position, int y)
  { (*pt2Object.*fpt)(r, position, y, picture_dir);};             // execute member function
};

template <typename TClass>
class TPRSAudioFunctor
{
 private:
  void (TClass::*fpt)(const std::string& r, const std::string& position, 
		      int y);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  
 public:
  
  TPRSAudioFunctor(TClass* _pt2Object, void(TClass::*_fpt)(const std::string&, const std::string&, 
							  int))
  { pt2Object = _pt2Object; fpt=_fpt; };

  void call(const std::string& r, const std::string& position, int y)
  { (*pt2Object.*fpt)(r, position, y);};             // execute member function
};

// search compare functions
template <typename TClass, typename T>
class CompareFunctor
{
 private:
  bool (TClass::*fpt)(const T&);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  
 public:
  
  CompareFunctor(TClass* _pt2Object, bool(TClass::*_fpt)(const T&))
  { pt2Object = _pt2Object; fpt=_fpt; };

  bool call(const T& s)
  { return (*pt2Object.*fpt)(s); };             // execute member function
};

template <typename TClass, typename T>
class RepFunctor
{
 private:
  std::string (TClass::*fpt)(const T&);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  
 public:
  
  RepFunctor(TClass* _pt2Object, std::string(TClass::*_fpt)(const T&))
  { pt2Object = _pt2Object; fpt=_fpt; };

  std::string call(const T& s)
  { return (*pt2Object.*fpt)(s); };             // execute member function
};

// generate_string templates

class TSaveAudioFunctor
{
 private:
  Input (*fpt)(const std::string&, int, Overlay&);   // pointer to member function
  Overlay o;       // pointer to object
  
 public:

  TSaveAudioFunctor(Input(*_fpt)(const std::string&, int, Overlay&))
    : o(Overlay("fixed size dialog"))
  { fpt=_fpt; };

  ~TSaveAudioFunctor()
  {
    Render *render = Render::get_instance();
    render->image_mut.enterMutex();
    o.cleanup();
    render->image_mut.leaveMutex();
  }

  Input call(const std::string& r)
  { return (*fpt)(gettext("Enter filename: ") + r, 450, o); };             // execute member function
};

template <typename TClass>
class MovieIMDbFunctor
{
 private:
  Input (TClass::*fpt)(std::string, IMDB_MOVIELIST, int, Overlay&);   // pointer to member function
  TClass* pt2Object;                  // pointer to object
  IMDB_MOVIELIST list;
  int p;
  Overlay &o;

 public:
  
  MovieIMDbFunctor(TClass* _pt2Object, Input(TClass::*_fpt)(std::string, IMDB_MOVIELIST, int, Overlay&), 
		   IMDB_MOVIELIST i, int pos, Overlay &overlay)
    : p(pos), o(overlay)
  { pt2Object = _pt2Object; fpt=_fpt; list = i; };

  Input call(const std::string& r)
  { return (*pt2Object.*fpt)(r, list, p, o); };             // execute member function
};

class TimeFv
{
public:
  virtual void call() = 0;
};

class TimeFi
{
public:
  virtual int call() = 0;
};

template <typename TClass>
class TimeFunctorv : public TimeFv
{
 private:
  void (TClass::*fpt)();   // pointer to member function
  TClass* pt2Object;                  // pointer to object

 public:
  
  TimeFunctorv(TClass* _pt2Object, void(TClass::*_fpt)())
  { pt2Object = _pt2Object; fpt=_fpt; };

  void call()
  { return (*pt2Object.*fpt)(); };             // execute member function
};

template <typename TClass>
class TimeFunctori : public TimeFi
{
 private:
  int (TClass::*fpt)();   // pointer to member function
  TClass* pt2Object;                  // pointer to object

 public:
  
  TimeFunctori(TClass* _pt2Object, int(TClass::*_fpt)())
  { pt2Object = _pt2Object; fpt=_fpt; };

  int call()
  { return (*pt2Object.*fpt)(); };             // execute member function
};

class TimeFunctorvNC : public TimeFv
{
 private:
  void (*fpt)();   // pointer to member function

 public:
  
  TimeFunctorvNC(void(*_fpt)())
  { fpt=_fpt; };

  void call()
  { return (*fpt)(); };             // execute member function
};

class TimeFunctoriNC : public TimeFi
{
 private:
  int (*fpt)();   // pointer to member function

 public:
  
  TimeFunctoriNC(int(*_fpt)())
  { fpt=_fpt; };

  int call()
  { return (*fpt)(); };             // execute member function
};

#endif
