#include <vtkm/cont/ArrayHandleCounting.h>

#include <vtkm/worklet/ScatterPermutation.h>
#include <vtkm/worklet/WorkletMapField.h>

#include <vtkm/cont/Invoker.h>

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

namespace
{

////
//// BEGIN-EXAMPLE ScatterPermutation.cxx
////
struct ReverseArrayWorklet : vtkm::worklet::WorkletMapField
{
  using ControlSignature = void(FieldIn inputArray, FieldOut outputArray);
  using ExecutionSignature = void(_1, _2);
  using InputDomain = _1;

  using ArrayStorageTag =
    typename vtkm::cont::ArrayHandleCounting<vtkm::Id>::StorageTag;
  using ScatterType = vtkm::worklet::ScatterPermutation<ArrayStorageTag>;

  VTKM_CONT
  static ScatterType MakeScatter(vtkm::Id arraySize)
  {
    return ScatterType(
      vtkm::cont::ArrayHandleCounting<vtkm::Id>(arraySize - 1, -1, arraySize));
  }

  template<typename FieldType>
  VTKM_EXEC void operator()(FieldType inputArrayField,
                            FieldType& outputArrayField) const
  {
    outputArrayField = inputArrayField;
  }
};

//
// Later in the associated Filter class...
//

//// PAUSE-EXAMPLE
struct DemoReverseArray
{
  vtkm::cont::Invoker Invoke;

  template<typename T, typename Storage>
  VTKM_CONT vtkm::cont::ArrayHandle<T> Run(
    const vtkm::cont::ArrayHandle<T, Storage>& inputField)
  {
    //// RESUME-EXAMPLE
    vtkm::cont::ArrayHandle<T> outputField;
    this->Invoke(ReverseArrayWorklet{},
                 ReverseArrayWorklet::MakeScatter(inputField.GetNumberOfValues()),
                 inputField,
                 outputField);
    ////
    //// END-EXAMPLE ScatterPermutation.cxx
    ////

    return outputField;
  }
};

void Run()
{
  std::cout << "Testing scatter permutation." << std::endl;
  vtkm::cont::ArrayHandleCounting<vtkm::Float32> inputArray(-2.5f, 0.1f, 51);
  vtkm::cont::ArrayHandleCounting<vtkm::Float32> resultArray(2.5f, -0.1f, 51);

  VTKM_TEST_ASSERT(inputArray.GetNumberOfValues() == 51,
                   "Unexpected number of input points.");

  vtkm::cont::ArrayHandle<vtkm::Float32> reversedArray =
    DemoReverseArray().Run(inputArray);

  VTKM_TEST_ASSERT(inputArray.GetNumberOfValues() ==
                     reversedArray.GetNumberOfValues(),
                   "Permutation array has wrong size.");
  auto portalScatter = reversedArray.GetPortalConstControl();
  auto portalAccepted = resultArray.GetPortalConstControl();

  for (vtkm::Id index = 0; index < portalScatter.GetNumberOfValues(); index++)
  {
    VTKM_TEST_ASSERT(test_equal(portalScatter.Get(index), portalAccepted.Get(index)),
                     "Permutation array has wrong value.");
  }
}

} // anonymous namespace

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