#include <vtkm/worklet/WorkletMapField.h>

#include <vtkm/filter/FilterField.h>

#include <vtkm/cont/Invoker.h>

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

namespace
{

constexpr vtkm::Id ARRAY_SIZE = 10;

////
//// BEGIN-EXAMPLE SimpleWorklet.cxx
////
//// LABEL Inherit
struct PoundsPerSquareInchToNewtonsPerSquareMeterWorklet
  : vtkm::worklet::WorkletMapField
{
  //// LABEL ControlSignature
  //// BEGIN-EXAMPLE ControlSignature.cxx
  using ControlSignature = void(FieldIn psi, FieldOut nsm);
  //// END-EXAMPLE ControlSignature.cxx
  //// LABEL ExecutionSignature
  //// BEGIN-EXAMPLE ExecutionSignature.cxx
  using ExecutionSignature = void(_1, _2);
  //// END-EXAMPLE ExecutionSignature.cxx
  //// LABEL InputDomain
  //// BEGIN-EXAMPLE InputDomain.cxx
  using InputDomain = _1;
  //// END-EXAMPLE InputDomain.cxx

  //// LABEL OperatorStart
  //// BEGIN-EXAMPLE WorkletOperator.cxx
  template<typename T>
  VTKM_EXEC void operator()(const T& psi, T& nsm) const
  {
    //// END-EXAMPLE WorkletOperator.cxx
    // 1 psi = 6894.76 N/m^2
    nsm = T(6894.76f) * psi;
    //// LABEL OperatorEnd
  }
};
////
//// END-EXAMPLE SimpleWorklet.cxx
////

void DemoWorklet()
{
  ////
  //// BEGIN-EXAMPLE WorkletInvoke.cxx
  ////
  vtkm::cont::ArrayHandle<vtkm::FloatDefault> psiArray;
  // Fill psiArray with values...
  //// PAUSE-EXAMPLE
  psiArray.Allocate(ARRAY_SIZE);
  SetPortal(psiArray.GetPortalControl());
  //// RESUME-EXAMPLE

  //// LABEL Construct
  vtkm::cont::Invoker invoke;

  vtkm::cont::ArrayHandle<vtkm::FloatDefault> nsmArray;
  //// LABEL Call
  invoke(PoundsPerSquareInchToNewtonsPerSquareMeterWorklet{}, psiArray, nsmArray);
  ////
  //// END-EXAMPLE WorkletInvoke.cxx
  ////
}

} // anonymous namespace

////
//// BEGIN-EXAMPLE SimpleField.cxx
////
namespace vtkm
{
namespace filter
{

class PoundsPerSquareInchToNewtonsPerSquareMeterFilter
  : public vtkm::filter::FilterField<
      PoundsPerSquareInchToNewtonsPerSquareMeterFilter>
{
public:
  VTKM_CONT PoundsPerSquareInchToNewtonsPerSquareMeterFilter();

  template<typename ArrayHandleType, typename Policy>
  VTKM_CONT vtkm::cont::DataSet DoExecute(
    const vtkm::cont::DataSet& inDataSet,
    const ArrayHandleType& inField,
    const vtkm::filter::FieldMetadata& fieldMetadata,
    vtkm::filter::PolicyBase<Policy>);
};

}
} // namespace vtkm::filter
////
//// END-EXAMPLE SimpleField.cxx
////

////
//// BEGIN-EXAMPLE SimpleFieldConstructor.cxx
////
VTKM_CONT vtkm::filter::PoundsPerSquareInchToNewtonsPerSquareMeterFilter::
  PoundsPerSquareInchToNewtonsPerSquareMeterFilter()
{
  this->SetOutputFieldName("");
}
////
//// END-EXAMPLE SimpleFieldConstructor.cxx
////

////
//// BEGIN-EXAMPLE SimpleFieldDoExecute.cxx
////
template<typename ArrayHandleType, typename Policy>
VTKM_CONT vtkm::cont::DataSet
vtkm::filter::PoundsPerSquareInchToNewtonsPerSquareMeterFilter::DoExecute(
  const vtkm::cont::DataSet& inDataSet,
  const ArrayHandleType& inField,
  const vtkm::filter::FieldMetadata& fieldMetadata,
  vtkm::filter::PolicyBase<Policy>)
{
  //// LABEL CheckArray
  VTKM_IS_ARRAY_HANDLE(ArrayHandleType);

  using ValueType = typename ArrayHandleType::ValueType;

  //// LABEL CreateOutputArray
  vtkm::cont::ArrayHandle<ValueType> outField;

  //// LABEL Invoke
  this->Invoke(
    PoundsPerSquareInchToNewtonsPerSquareMeterWorklet{}, inField, outField);

  //// LABEL OutputName
  std::string outFieldName = this->GetOutputFieldName();
  if (outFieldName == "")
  {
    outFieldName = fieldMetadata.GetName() + "_N/m^2";
  }

  //// LABEL CreateResult
  return vtkm::filter::CreateResult(
    inDataSet, outField, outFieldName, fieldMetadata);
}
////
//// END-EXAMPLE SimpleFieldDoExecute.cxx
////

namespace
{

void DemoFilter()
{
  vtkm::cont::testing::MakeTestDataSet makeData;
  vtkm::cont::DataSet inData = makeData.Make3DExplicitDataSet0();

  vtkm::filter::PoundsPerSquareInchToNewtonsPerSquareMeterFilter convertFilter;
  convertFilter.SetActiveField("pointvar");
  vtkm::cont::DataSet outData = convertFilter.Execute(inData);

  outData.PrintSummary(std::cout);
  VTKM_TEST_ASSERT(outData.HasPointField("pointvar_N/m^2"));
}

void Run()
{
  DemoWorklet();
  DemoFilter();
}

} // anonymous namespace

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