//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

#ifndef smtk_graph_TypeTraits_h
#define smtk_graph_TypeTraits_h

#include <functional>
#include <type_traits>

namespace smtk
{
namespace graph
{

template<class...>
struct conjunction : std::true_type
{
};
template<class B1>
struct conjunction<B1> : B1
{
};
template<class B1, class... Bn>
struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type
{
};

template<typename T, typename... Ts>
using CompatibleTypes =
  typename std::enable_if<conjunction<std::is_convertible<Ts, T>...>::value>::type;

template<typename Iterable>
class is_iterable
{
  template<typename X>
  static std::true_type testIterable(
    decltype(std::distance(std::declval<X>(), std::declval<X>()))*);
  template<typename X>
  static std::false_type testIterable(...);

public:
  using type = decltype(testIterable<Iterable>(nullptr));
  static constexpr bool value = type::value;
};

template<typename Container>
class is_container
{
  template<typename X>
  static std::true_type testContainer(
    decltype(std::distance(std::declval<X>().begin(), std::declval<X>().end()))*);
  template<typename X>
  static std::false_type testContainer(...);

public:
  using type = decltype(testContainer<Container>(nullptr));
  static constexpr bool value = type::value;
};

template<typename API, typename Functor, typename Input>
class has_custom_visit
{
  template<typename X>
  static std::true_type testVisitable(
    decltype(std::declval<X>().visit(std::declval<Input>(), std::declval<const Functor&>()))*);
  template<typename X>
  static std::false_type testVisitable(...);

public:
  using type = decltype(testVisitable<API>(nullptr));
  static constexpr bool value = type::value;
};

template<typename Functor, typename Input>
class accepts
{
  template<typename X>
  static std::true_type testAccepts(decltype(std::declval<X>()(std::declval<Input>()))*);
  template<typename X>
  static std::false_type testAccepts(...);

public:
  using type = decltype(testAccepts<Functor>(nullptr));
  static constexpr bool value = type::value;
};
} // namespace graph
} // namespace smtk

#endif
