#include <vtkm/cont/ImplicitFunctionHandle.h>

#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/WorkletMapField.h>

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

namespace
{

class CutImplicitFunction : public vtkm::worklet::WorkletMapField
{
public:
  using ControlSignature = void(FieldIn, FieldOut);
  using ExecutionSignature = void(_1, _2);

  CutImplicitFunction(const vtkm::ImplicitFunction* function)
    : Function(function)
  {
  }

  template<typename VecType>
  VTKM_EXEC void operator()(const VecType& point, VecType& ret) const
  {
    if (this->Function->Value(point) > 0)
    {
      ret = point;
    }
    else
    {
      ret = VecType(0, 0, 0);
    }
  }

private:
  const vtkm::ImplicitFunction* Function;
};

void Run()
{
  using vtkm::cont::testing::implicit_function_detail::TestArrayEqual;
  constexpr vtkm::Id nVerts = 16;
  vtkm::Vec3f_32 data[nVerts] = {
    vtkm::Vec3f_32(1, 1, 1),  vtkm::Vec3f_32(1, 1, 0),  vtkm::Vec3f_32(1, 1, -1),
    vtkm::Vec3f_32(1, 1, -2), vtkm::Vec3f_32(1, 2, 1),  vtkm::Vec3f_32(1, 2, 0),
    vtkm::Vec3f_32(1, 2, -1), vtkm::Vec3f_32(1, 2, -2), vtkm::Vec3f_32(2, 1, 1),
    vtkm::Vec3f_32(2, 1, 0),  vtkm::Vec3f_32(2, 1, -1), vtkm::Vec3f_32(2, 1, -2),
    vtkm::Vec3f_32(2, 2, 1),  vtkm::Vec3f_32(2, 2, 0),  vtkm::Vec3f_32(2, 2, -1),
    vtkm::Vec3f_32(2, 2, -2),
  };
  ////
  //// BEGIN-EXAMPLE ImplicitFunctionHandle.cxx
  ////
  auto coordinates =
    vtkm::cont::make_CoordinateSystem("coordinates", data, 16, vtkm::CopyFlag::On);
  auto handle = vtkm::cont::make_ImplicitFunctionHandle(
    vtkm::Sphere({ 2.0f, 2.0f, -2.0f }, 2.0f));

  vtkm::cont::ArrayHandle<vtkm::Vec3f> internalPoints;
  CutImplicitFunction func(
    handle.PrepareForExecution(vtkm::cont::DeviceAdapterTagSerial()));

  vtkm::worklet::DispatcherMapField<CutImplicitFunction> dispatcher(func);
  dispatcher.Invoke(coordinates, internalPoints);
  ////
  //// END-EXAMPLE ImplicitFunctionHandle.cxx
  ////

  std::array<vtkm::Vec3f, 16> values = { { { 1.0f, 1.0f, 1.0f },
                                           { 1.0f, 1.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 1.0f, 2.0f, 1.0f },
                                           { 1.0f, 2.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 2.0f, 1.0f, 1.0f },
                                           { 2.0f, 1.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 2.0f, 2.0f, 1.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f },
                                           { 0.0f, 0.0f, 0.0f } } };
  VTKM_TEST_ASSERT(TestArrayEqual(internalPoints, values),
                   "Results don't match internal values.");
}

} // anonymous namespace

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