//============================================================================
//  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.
//============================================================================
$# This file uses the pyexpander macro processing utility to build the
$# FunctionInterface facilities that use a variable number of arguments.
$# Information, documentation, and downloads for pyexpander can be found at:
$#
$#     http://pyexpander.sourceforge.net/
$#
$# To build the source code, execute the following (after installing
$# pyexpander, of course):
$#
$#     expander.py VariantDetail.h.in > VariantDetail.h
$#
$# Ignore the following comment. It is meant for the generated file.
// **** DO NOT EDIT THIS FILE!!! ****
// This file is automatically generated by VariantDetail.h.in

#ifndef vtk_m_internal_VariantDetail_h
#define vtk_m_internal_VariantDetail_h

#ifndef vtk_m_internal_Variant_h
#error VariantDetail.h must be included from Variant.h
#endif

#include <vtkm/Types.h>

#include <vtkm/internal/brigand.hpp>

#include <type_traits>

$py(max_expanded=20)\

$# Python commands used in template expansion.
$py(
def type_list(num_params):
  if num_params < 0:
    return ''
  result = 'T0'
  for param in range(1, num_params + 1):
    result += ', T%d' % param
  return result
)\
$#
$extend(type_list)\

namespace vtkm
{
namespace internal
{
namespace detail
{

template <typename ReturnType>
struct VariantDummyReturn
{
  VTKM_EXEC_CONT static inline ReturnType F() noexcept { return ReturnType{}; }
};
template <>
struct VariantDummyReturn<void>
{
  VTKM_EXEC_CONT static inline void F() noexcept {}
};

template <typename ReturnType, typename Functor, typename... Args>
VTKM_EXEC_CONT inline ReturnType VariantCastAndCallImpl(brigand::list<>,
                                                        vtkm::IdComponent,
                                                        Functor&&,
                                                        const void*,
                                                        Args&&...) noexcept
{
  // If we are here, it means we failed to find the appropriate type in a variant
  VTKM_ASSERT(false && "Internal error, bad Variant state.");
  return VariantDummyReturn<ReturnType>::F();
}

// clang-format off

$for(num_params in range(0, max_expanded))\
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename ReturnType,
$for(param_index in range(0, num_params + 1))\
          typename T$(param_index),
$endfor\
          typename Functor,
          typename... Args>
VTKM_EXEC_CONT inline ReturnType VariantCastAndCallImpl(
  brigand::list<$type_list(num_params)>,
  vtkm::IdComponent index,
  Functor&& f,
  const void* storage,
  Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
{
  switch (index)
  {
$for(param_index in range(0, num_params + 1))\
    case $(param_index):
      return f(*reinterpret_cast<const T$(param_index)*>(storage), std::forward<Args>(args)...);
$endfor\
    default:
      // If we are here, it means we failed to find the appropriate type in a variant
      VTKM_ASSERT(false && "Internal error, bad Variant state.");
      return VariantDummyReturn<ReturnType>::F();
  }
}

VTKM_SUPPRESS_EXEC_WARNINGS
template <typename ReturnType,
$for(param_index in range(0, num_params + 1))\
          typename T$(param_index),
$endfor\
          typename Functor,
          typename... Args>
VTKM_EXEC_CONT inline ReturnType VariantCastAndCallImpl(
  brigand::list<$type_list(num_params)>,
  vtkm::IdComponent index,
  Functor&& f,
  void* storage,
  Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
{
  switch (index)
  {
$for(param_index in range(0, num_params + 1))\
    case $(param_index):
      return f(*reinterpret_cast<T$(param_index)*>(storage), std::forward<Args>(args)...);
$endfor\
    default:
      // If we are here, it means we failed to find the appropriate type in a variant
      VTKM_ASSERT(false && "Internal error, bad Variant state.");
      return VariantDummyReturn<ReturnType>::F();
  }
}

$endfor\
//clang-format on

// Recurse for cases where Variant has more than $(max_expanded) types
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename ReturnType,
$for(param_index in range(0, max_expanded + 1))\
          typename T$(param_index),
$endfor\
          typename... RemainingT,
          typename Functor,
          typename... Args>
VTKM_EXEC_CONT inline ReturnType VariantCastAndCallImpl(
  brigand::list<$type_list(max_expanded), RemainingT...>,
  vtkm::IdComponent index,
  Functor&& f,
  const void* storage,
  Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
{
  if (index < $(max_expanded))
  {
    return VariantCastAndCallImpl<ReturnType>(
      brigand::list<$type_list(max_expanded - 1)>{},
      index,
      f,
      storage,
      args...);
  }
  else
  {
    return VariantCastAndCallImpl<ReturnType>(
      brigand::list<T$(max_expanded), RemainingT...>{}, index - $(max_expanded), f, storage, args...);
  }
}

VTKM_SUPPRESS_EXEC_WARNINGS
template <typename ReturnType,
$for(param_index in range(0, max_expanded + 1))\
          typename T$(param_index),
$endfor\
          typename... RemainingT,
          typename Functor,
          typename... Args>
VTKM_EXEC_CONT inline ReturnType VariantCastAndCallImpl(
  brigand::list<$type_list(max_expanded), RemainingT...>,
  vtkm::IdComponent index,
  Functor&& f,
  void* storage,
  Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
{
  if (index < $(max_expanded))
  {
    return VariantCastAndCallImpl<ReturnType>(
      brigand::list<$type_list(max_expanded - 1)>{},
      index,
      f,
      storage,
      args...);
  }
  else
  {
    return VariantCastAndCallImpl<ReturnType>(
      brigand::list<T$(max_expanded), RemainingT...>{}, index - $(max_expanded), f, storage, args...);
  }
}

}
}
} // vtkm::internal::detail

#endif //vtk_m_internal_VariantDetail_h
