#include <vtkm/cont/arg/TransportTagArrayIn.h>
#include <vtkm/cont/arg/TransportTagArrayOut.h>
#include <vtkm/cont/arg/TransportTagWholeArrayInOut.h>
#include <vtkm/cont/arg/TypeCheckTagArray.h>
#include <vtkm/cont/arg/TypeCheckTagExecObject.h>

#include <vtkm/cont/DeviceAdapter.h>

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

namespace
{

namespace TypeCheckNamespace
{

////
//// BEGIN-EXAMPLE TypeCheck.cxx
////
struct MyExecObject : vtkm::cont::ExecutionObjectBase
{
  vtkm::Id Value;
};

void DoTypeChecks()
{
  ////
  //// PAUSE-EXAMPLE
  ////
  std::cout << "Testing type checks" << std::endl;
  ////
  //// RESUME-EXAMPLE
  ////
  using vtkm::cont::arg::TypeCheck;
  using vtkm::cont::arg::TypeCheckTagArray;
  using vtkm::cont::arg::TypeCheckTagExecObject;

  bool check1 = TypeCheck<TypeCheckTagExecObject, MyExecObject>::value; // true
  bool check2 = TypeCheck<TypeCheckTagExecObject, vtkm::Id>::value;     // false

  using ArrayType = vtkm::cont::ArrayHandle<vtkm::Float32>;

  bool check3 = TypeCheck<TypeCheckTagArray, ArrayType>::value;      // true
  bool check4 = TypeCheck<TypeCheckTagExecObject, ArrayType>::value; // false
  ////
  //// PAUSE-EXAMPLE
  ////
  VTKM_TEST_ASSERT(check1 == true, "Type check failed.");
  VTKM_TEST_ASSERT(check2 == false, "Type check failed.");
  VTKM_TEST_ASSERT(check3 == true, "Type check failed.");
  VTKM_TEST_ASSERT(check4 == false, "Type check failed.");
  ////
  //// RESUME-EXAMPLE
  ////
}
////
//// END-EXAMPLE TypeCheck.cxx
////

} // namespace TypeCheckNamespace

using namespace TypeCheckNamespace;

namespace TransportNamespace
{

////
//// BEGIN-EXAMPLE Transport.cxx
////
template<typename ArrayType, typename Device>
void DoTransport(ArrayType inArray, ArrayType outArray, Device)
{
  VTKM_IS_ARRAY_HANDLE(ArrayType);
  VTKM_IS_DEVICE_ADAPTER_TAG(Device);
  ////
  //// PAUSE-EXAMPLE
  ////
  std::cout << "Testing transports." << std::endl;
  ////
  //// RESUME-EXAMPLE
  ////

  using vtkm::cont::arg::Transport;
  using vtkm::cont::arg::TransportTagArrayIn;
  using vtkm::cont::arg::TransportTagArrayOut;
  using vtkm::cont::arg::TransportTagWholeArrayInOut;

  // The array in transport returns a read-only array portal.
  using ArrayInTransport = Transport<TransportTagArrayIn, ArrayType, Device>;
  typename ArrayInTransport::ExecObjectType inPortal =
    ArrayInTransport()(inArray, inArray, 10, 10);

  // The array out transport returns an allocated array portal.
  using ArrayOutTransport = Transport<TransportTagArrayOut, ArrayType, Device>;
  typename ArrayOutTransport::ExecObjectType outPortal =
    ArrayOutTransport()(outArray, inArray, 10, 10);

  // The whole array in transport returns a read-only array portal wrapped in
  // a vtkm::exec::ExecutionWholeArrayConst.
  using WholeArrayTransport =
    Transport<TransportTagWholeArrayInOut, ArrayType, Device>;
  typename WholeArrayTransport::ExecObjectType wholeArray =
    WholeArrayTransport()(inArray, inArray, 10, 10);
  ////
  //// PAUSE-EXAMPLE
  ////
  CheckPortal(inPortal);
  VTKM_TEST_ASSERT(outPortal.GetNumberOfValues() == 10, "Bad array out.");
  CheckPortal(wholeArray.GetPortal());
  ////
  //// RESUME-EXAMPLE
  ////
}
////
//// END-EXAMPLE Transport.cxx
////

void DoTransport()
{
  vtkm::Id buffer[10];
  for (vtkm::Id index = 0; index < 10; index++)
  {
    buffer[index] = TestValue(index, vtkm::Id());
  }

  DoTransport(vtkm::cont::make_ArrayHandle(buffer, 10),
              vtkm::cont::ArrayHandle<vtkm::Id>(),
              vtkm::cont::DeviceAdapterTagSerial());
}

} // namespace TransportNamespace

using namespace TransportNamespace;

void Test()
{
  DoTypeChecks();
  DoTransport();
}

} // anonymous namespace

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