#pragma once
#include "Utils.h"
#include <functional>

/**
 * @brief Adaptor functor that dereferences a pointer.
 * @tparam T Type of pointer to dereference.
 */
template<class T>
struct dereference : std::unary_function<T*,T&>
{
  result_type operator()(argument_type arg) const { return *arg; }
};

/**
 * @brief Adaptor functor that returns the address of an object.
 * @tparam T Type of object to return the address of.
 */
template<class T>
struct address_of : std::unary_function<T&,T*>
{
  result_type operator()(argument_type arg) const { return &arg; }
};

/**
 * @brief Predicate that checks whether a value is between two others.
 * @tparam T The type of values.
 * @tparam Inclusive Whether the interval is closed (inclusive).
 */
template<class T, bool Inclusive=false>
struct is_between : std::unary_function<T,bool>
{
  typename static_if_else<
    Inclusive,
    std::less_equal<T>,
    std::less<T>
  >::type comparator;

  is_between(const T& v1, const T& v2) : m_v1(v1), m_v2(v2) { }

  const bool operator()(const T& v) const
  {
    return (comparator(m_v1, v) && comparator(v, m_v2))
        || (comparator(m_v2, v) && comparator(v, m_v1));
  }

protected:
  const T m_v1, m_v2;

private: 
  //const member prevent the use of the assigment operator
  is_between& operator=(const is_between&);
};

/**
 * @brief Functor that returns the first (non-const) value of a non-const pair.
 * @tparam Pair The type of pair.
 */
template <class Pair>
struct extract_pair_first : public std::unary_function<Pair, typename Pair::first_type&>
{
  typename Pair::first_type& operator()(Pair& p) const
  {
    return p.first;
  }
};
/**
 * @brief Functor that returns the first (const) value of a const pair.
 * @tparam Pair The type of pair.
 */
template <class Pair>
struct extract_pair_first_const : public std::unary_function<Pair, typename const Pair::first_type&>
{
  extract_pair_first_const() {}
  extract_pair_first_const(const extract_pair_first<Pair>&) {}

  typename const Pair::first_type& operator()(const Pair& p) const
  {
    return p.first;
  }
};

/**
 * @brief Functor that returns the second (non-const) value of a non-const pair.
 * @tparam Pair The type of pair.
 */
template <class Pair>
struct extract_pair_second : public std::unary_function<Pair, typename Pair::second_type&>
{
  typename Pair::second_type& operator()(Pair& p) const
  {
    return p.second;
  }
};
/**
 * @brief Functor that returns the second (const) value of a const pair.
 * @tparam Pair The type of pair.
 */
template <class Pair>
struct extract_pair_second_const : public std::unary_function<Pair, typename const Pair::second_type&>
{
  extract_pair_second_const() {}
  extract_pair_second_const(const extract_pair_second<Pair>&) {}

  typename const Pair::second_type& operator()(const Pair& p) const
  {
    return p.second;
  }
};

/**
 * @brief Adaptor functor that combines two compatible functors.
 * @tparam Functor1 The first type of functor.
 * @tparam Functor2 The second type of functor.
 */
template <class Functor1, class Functor2>
struct composite_functor : public std::unary_function<typename Functor2::argument_type, typename Functor1::result_type>
{
  composite_functor(Functor1 f1 = Functor1(), Functor2 f2 = Functor2())
    : m_f1(f1)
    , m_f2(f2)
  {}
  template <class OtherFunctor1, class OtherFunctor2>
  composite_functor(const composite_functor<OtherFunctor1, OtherFunctor2>& other)
    : m_f1(other.m_f1)
    , m_f2(other.m_f2)
  {}
  typename Functor1::result_type operator()(typename Functor2::argument_type arg) const
  {
    return m_f1(m_f2(arg));
  }

  Functor1 m_f1;
  Functor2 m_f2;

private:
  composite_functor & operator=(const composite_functor &);
};