/*
 * Copyright (c) 2014-2015, Siemens AG. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef EMBB_BASE_FUNCTION_H_
#define EMBB_BASE_FUNCTION_H_

/**
 * \defgroup CPP_BASE_FUNCTION Function
 * %Function wrapper and binding of parameters.
 *
 * \ingroup CPP_BASE
 */

namespace embb {
namespace base {

/**
 * Provides placeholders for Function arguments used in Bind()
 *
 * \ingroup CPP_BASE_FUNCTION
 */
class Placeholder {
 public:
  class Arg_1 {};
  class Arg_2 {};
  class Arg_3 {};
  class Arg_4 {};
  class Arg_5 {};

  /**
   * Placeholder variable to be used in Bind() for keeping one argument unbound
   */
  static Arg_1 _1;

  /**
   * Placeholder variable to be used in Bind() for keeping one argument unbound
   */
  static Arg_2 _2;

  /**
   * Placeholder variable to be used in Bind() for keeping one argument unbound
   */
  static Arg_3 _3;

  /**
   * Placeholder variable to be used in Bind() for keeping one argument unbound
   */
  static Arg_4 _4;

  /**
   * Placeholder variable to be used in Bind() for keeping one argument unbound
   */
  static Arg_5 _5;
};

} // namespace base
} // namespace embb

#ifdef DOXYGEN

namespace embb {
namespace base {

/**
 * Wraps function pointers, member function pointers, and functors with up to
 * five arguments.
 *
 * \ingroup CPP_BASE_FUNCTION
 */
template <typename ReturnType, ...>
class Function {
 public:
  /**
   * Constructor from functor. Uses operator() with return type ReturnType
   * and up to five arguments. Copies the functor.
   * \memory Allocates memory for the copy of the functor.
   */
  template <class ClassType>
  explicit Function(
    ClassType const & obj              /**< The functor to wrap. */
    );

  /**
   * Constructor from function pointer with return type ReturnType and up to
   * five arguments.
   */
  explicit Function(
    ReturnType(*func)(...)             /**< The function pointer. */
    );

  /**
   * Constructor from object and member function pointer with return type
   * ReturnType and up to five arguments.
   */
  template <class ClassType>
  Function(
    ClassType & obj,                   /**< Reference to object. */
    ReturnType(ClassType::*func)(...)
                                       /**< Member function pointer. */
    );

  /**
   * Copy constructor.
   */
  Function(
    Function const & func              /**< The Function to copy. */
    );

  /**
   * Destructor.
   */
  ~Function();

  /**
   * Assigns this object a new function pointer.
   */
  void operator = (
    ReturnType(*func)(...)             /**< The function pointer. */
    );

  /**
   * Assigns this object another Function.
   */
  void operator = (
    Function & func                    /**< The Function. */
    );

  /**
   * Assigns this object a new functor. The functor is copied.
   */
  template <class C>
  void operator = (
    C const & obj                      /**< The functor. */
    );

  /**
   * Calls the wrapped function with the given parameters.
   * \returns A value generated by the wrapped function.
   */
  ReturnType operator () (...);
};

/**
 * Wraps an object and a member function pointer into a Function
 *
 * \returns Function with same return value and argument syntax as \c func
 * \see Function
 * \tparam ClassType Class that contains the member function pointed to by \c
 *         func.
 * \tparam ReturnType Return type of member function pointed to by \c func
 * \tparam [Arg1,...,Arg5] (Optional) Types of up to five arguments of the
 *         member function
 * \ingroup CPP_BASE_FUNCTION
 */
template <class ClassType, typename ReturnType, ...>
Function<ReturnType, [Arg1, ..., Arg5]> MakeFunction(
  ClassType& obj,
  /**< [IN] Reference to the object with corresponding member function */
  ReturnType(ClassType::*func)([Arg1, ..., Arg5])
  /**< [IN] Member function pointer with up to five optional arguments */
  );

/**
 * Wraps a function pointer into a Function
 *
 * \returns Function with same return value and argument syntax as \c func
 * \see Function
 * \tparam ReturnType Return type of member function pointed to by \c func.
 * \tparam [Arg1,...,Arg5] (Optional) Types of up to five arguments of the
 *         member function
 * \ingroup CPP_BASE_FUNCTION
 */
template <typename ReturnType, ...>
Function<ReturnType, [Arg1, ..., Arg5]> MakeFunction(
  ReturnType(*func)([Arg1, ..., Arg5])
  /**< [IN] Function pointer with up to five optional arguments */
  );

/**
 * Binds given values as arguments of \c func into a new Function
 *
 * The new Function has no arguments or one, if Placeholder::_1 is
 * given as one of the values. The position of Placeholder::_1 determines which
 * argument of \c func is not bound.
 * \memory Allocates dynamic memory to hold the parameters.
 * \returns Function that uses given values as parameters
 * \see Placeholder, Function
 * \tparam ReturnType Return type of \c func and parameterless function returned
 * \tparam [UnboundArgument] Type of not bound argument of \c func, only present
 *         when a placeholder is used as value in the bind.
 * \tparam Arg1[,...,Arg5] Types of up to five arguments of the values to bind
 * \ingroup CPP_BASE_FUNCTION
 */
template <typename ReturnType, UnboundArgument, Arg1, ...>
Function<ReturnType[, UnboundArgument]> Bind(
  Function<ReturnType, Arg1[, ..., Arg5]> func,
  /**< [IN] The Function to bind the values (\c value1, ...) to */
  Arg1 value1,
  /**< [IN] At least one and up to five values to bind as arguments of \c func.
            Placeholder::_1 can be used instead of one of the values to keep the
            corresponding argument of \c func unbound. */
  ...
  );

} // namespace base
} // namespace embb

#else // DOXYGEN

#include <embb/base/internal/function0.h>
#include <embb/base/internal/function1.h>
#include <embb/base/internal/function2.h>
#include <embb/base/internal/function3.h>
#include <embb/base/internal/function4.h>
#include <embb/base/internal/function5.h>

#endif // DOXYGEN

#endif // EMBB_BASE_FUNCTION_H_
