#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/TryExecute.h>

#include <vtkm/cont/testing/Testing.h>

namespace TryExecuteExample
{

////
//// BEGIN-EXAMPLE ArrayAverageImpl.cxx
////
template<typename T, typename Storage, typename Device>
VTKM_CONT T ArrayAverage(const vtkm::cont::ArrayHandle<T, Storage>& array, Device)
{
  T sum = vtkm::cont::Algorithm::Reduce(array, T(0));
  return sum / T(array.GetNumberOfValues());
}
////
//// END-EXAMPLE ArrayAverageImpl.cxx
////

////
//// BEGIN-EXAMPLE ArrayAverageTryExecute.cxx
////
namespace detail
{

struct ArrayAverageFunctor
{
  template<typename Device, typename T, typename Storage>
  VTKM_CONT bool operator()(Device,
                            const vtkm::cont::ArrayHandle<T, Storage>& inArray,
                            T& outValue) const
  {
    // Call the version of ArrayAverage that takes a DeviceAdapter.
    outValue = ArrayAverage(inArray, Device());

    return true;
  }
};

} // namespace detail

template<typename T, typename Storage>
VTKM_CONT T ArrayAverage(const vtkm::cont::ArrayHandle<T, Storage>& array)
{
  T outValue;

  bool foundAverage =
    vtkm::cont::TryExecute(detail::ArrayAverageFunctor{}, array, outValue);

  if (!foundAverage)
  {
    throw vtkm::cont::ErrorExecution("Could not compute array average.");
  }

  return outValue;
}
////
//// END-EXAMPLE ArrayAverageTryExecute.cxx
////

void Run()
{
  static const vtkm::Id ARRAY_SIZE = 11;

  std::cout << "Running average on " << ARRAY_SIZE << " indices" << std::endl;
  vtkm::Id average = ArrayAverage(vtkm::cont::ArrayHandleIndex(ARRAY_SIZE));
  VTKM_TEST_ASSERT(average == (ARRAY_SIZE - 1) / 2, "Bad average");
}

} // namespace TryExecuteExample

int TryExecute(int argc, char* argv[])
{
  return vtkm::cont::testing::Testing::Run(TryExecuteExample::Run, argc, argv);
}
