/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkEnableIf_h
#define itkEnableIf_h

#include "itkMacro.h"

#if !defined(ITK_LEGACY_REMOVE)

namespace itk
{
/// \cond HIDE_META_PROGRAMMING
namespace mpl
{

/** \brief This is an implementation of the enable if idiom.
 * \ingroup MetaProgrammingLibrary
 * \ingroup ITKCommon
 *
 * Enable if is a common C++ meta-programming technique for example
 * there is a boost implementation, for more information on its usage
 * please see:
 * https://www.boost.org/doc/libs/1_49_0/libs/utility/enable_if.html
 *
 * This template enables specialization of a templated function based
 * on some traits or concepts. It is implemented with SFINAE.
 *
 * If the parameter V is true then the Type trait is the second
 * template parameter, otherwise an implementation does not exist and
 * with SFINAE another implementation may be chosen.
 *
 * Example:
 \code
 template< typename TType>
   typename EnableIfC<
     IsSame<TType, typename NumericTraits<TType>::ValueType>::Value,
     TType >::Type
 GetComponent(const TType pix,
              unsigned int itkNotUsed( idx ) ) const
 {
   return pix;
 }
 \endcode
 *
 * \sa \c EnableIf
 */
template <bool V, typename TType = void>
struct EnableIfC
{};
/// \cond SPECIALIZATION_IMPLEMENTATION
template <typename TType>
struct EnableIfC<true, TType>
{
  using Type = TType;
};
/// \endcond


/** \brief An implementation of the negation of the enable if idiom.
 * \ingroup MetaProgrammingLibrary
 * \ingroup ITKCommon
 *
 * \sa \c EnableIfC
 * \sa \c DisableIf
 */
template <bool V, typename TType = void>
struct DisableIfC
{};
/// \cond SPECIALIZATION_IMPLEMENTATION
template <typename TType>
struct DisableIfC<false, TType>
{
  using Type = TType;
};
/// \endcond

/** \brief simplified way to dispose of \c enable_if.
 * \ingroup MetaProgrammingLibrary
 * \ingroup ITKCommon
 * \tparam TCondition Condition type. It's expected to provide a boolean value
 * through its \c Value member.
 * \tparam TType      Type to \em return when the \c TCondition is (a) true (type).
 *
 * This \em overload automatically fetches \c TCondition value. However, beware, it
 * won't work with standard C++ traits or boost traits. Indeed, this \c
 * enable_if overload expects the \em value to follow UpperCamelCase ITK naming
 * policy instead of the standard snake_case policy.
 *
 * Example:
   \code
   template< typename TType>
     typename EnableIf<
       IsSame<TType, typename NumericTraits<TType>::ValueType>,
       TType >::Type
   GetComponent(const TType pix,
                unsigned int itkNotUsed( idx ) ) const
   {
     return pix;
   }
   \endcode
 * \sa \c EnableIfC
 * \sa \c DisableIf
 */
template <class TCondition, class TType = void>
struct EnableIf : public EnableIfC<TCondition::Value, TType>
{};

/** \brief simplified way to dispose of \c disable_if.
 * \ingroup MetaProgrammingLibrary
 * \ingroup ITKCommon
 * \tparam TCondition Condition type. It's expected to provide a boolean value
 * through its \c Value member.
 * \tparam TType      Type to \em return when the \c TCondition is (a) false (type).
 *
 * This \em overload automatically fetches \c TCondition value. However, beware, it
 * won't work with standard C++ traits or boost traits. Indeed, this \c
 * enable_if overload expects the \em value to follow UpperCamelCase ITK naming
 * policy instead of the standard snake_case policy.
 *
 * Example:
   \code
   template< typename TType>
     typename DisableIf<
       mpl::Not_<IsSame<TType, typename NumericTraits<TType>::ValueType>>,
       TType >::Type
   GetComponent(const TType pix,
                unsigned int itkNotUsed( idx ) ) const
   {
     return pix;
   }
   \endcode
 * \sa \c EnableIfC
 * \sa \c DisableIf
 */
template <class TCondition, class TType = void>
struct DisableIf : public DisableIfC<TCondition::Value, TType>
{};

} // namespace mpl

// itk::EnableIf(C), DisableIf(C) have move to itk::mpl
// Expect them to be deprecated.
using mpl::EnableIf;
using mpl::DisableIf;
using mpl::EnableIfC;
using mpl::DisableIfC;

/// \endcond
} // namespace itk

#else // ITK_LEGACY_REMOVE
#  error Use C++ 11 std::enable_if directly
#endif

#endif // itkEnableIf_h
