#include <vtkm/cont/ArrayHandleSwizzle.h>

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

namespace
{

template<typename ArrayHandleType>
void CheckArray(const ArrayHandleType array)
{
  VTKM_TEST_ASSERT(array.GetNumberOfValues() == 3, "Permuted array has wrong size.");

  auto portal = array.GetPortalConstControl();
  VTKM_TEST_ASSERT(portal.GetNumberOfValues() == 3,
                   "Permuted portal has wrong size.");

  using Vec = vtkm::Vec<vtkm::Float64, 3>;

  VTKM_TEST_ASSERT(test_equal(portal.Get(0), Vec(0.2, 0.0, 0.3)),
                   "Permuted array has wrong value.");
  VTKM_TEST_ASSERT(test_equal(portal.Get(1), Vec(1.2, 1.0, 1.3)),
                   "Permuted array has wrong value.");
  VTKM_TEST_ASSERT(test_equal(portal.Get(2), Vec(2.2, 2.0, 2.3)),
                   "Permuted array has wrong value.");
}

void Test()
{
  ////
  //// BEGIN-EXAMPLE ArrayHandleSwizzle.cxx
  ////
  using ValueArrayType = vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float64, 4>>;

  // Create array with values
  // [ (0.0, 0.1, 0.2, 0.3), (1.0, 1.1, 1.2, 1.3), (2.0, 2.1, 2.2, 2.3) ]
  ValueArrayType valueArray;
  valueArray.Allocate(3);
  auto valuePortal = valueArray.GetPortalControl();
  valuePortal.Set(0, vtkm::make_Vec(0.0, 0.1, 0.2, 0.3));
  valuePortal.Set(1, vtkm::make_Vec(1.0, 1.1, 1.2, 1.3));
  valuePortal.Set(2, vtkm::make_Vec(2.0, 2.1, 2.2, 2.3));

  // Use ArrayHandleSwizzle to make an array of Vec-3 with x,y,z,w swizzled to z,x,w
  // [ (0.2, 0.0, 0.3), (1.2, 1.0, 1.3), (2.2, 2.0, 2.3) ]
  vtkm::cont::ArrayHandleSwizzle<ValueArrayType, 3> swizzledArray(
    valueArray, vtkm::Vec<vtkm::IdComponent, 3>(2, 0, 3));
  ////
  //// END-EXAMPLE ArrayHandleSwizzle.cxx
  ////
  CheckArray(swizzledArray);

  CheckArray(
    ////
    //// BEGIN-EXAMPLE MakeArrayHandleSwizzle.cxx
    ////
    vtkm::cont::make_ArrayHandleSwizzle(valueArray, 2, 0, 3)
    ////
    //// END-EXAMPLE MakeArrayHandleSwizzle.cxx
    ////
  );
}

} // anonymous namespace

int ArrayHandleSwizzle(int, char* [])
{
  return vtkm::cont::testing::Testing::Run(Test);
}
