#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/DynamicArrayHandle.h>

#include <vtkm/cont/internal/StorageError.h>

#include <vtkm/VecTraits.h>

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

namespace {

////
//// BEGIN-EXAMPLE CreateDynamicArrayHandle.cxx
////
VTKM_CONT
vtkm::cont::DynamicArrayHandle
LoadDynamicArray(const void *buffer, vtkm::Id length, std::string type)
{
  vtkm::cont::DynamicArrayHandle handle;
  if (type == "float")
  {
    vtkm::cont::ArrayHandle<vtkm::Float32> concreteArray =
        vtkm::cont::make_ArrayHandle(
          reinterpret_cast<const vtkm::Float32*>(buffer), length);
    handle = concreteArray;
  } else if (type == "int") {
    vtkm::cont::ArrayHandle<vtkm::Int32> concreteArray =
        vtkm::cont::make_ArrayHandle(
          reinterpret_cast<const vtkm::Int32*>(buffer), length);
    handle = concreteArray;
  }
  return handle;
}
////
//// END-EXAMPLE CreateDynamicArrayHandle.cxx
////

void TryLoadDynamicArray()
{
  vtkm::Float32 scalarBuffer[10];
  vtkm::cont::DynamicArrayHandle handle =
      LoadDynamicArray(scalarBuffer, 10, "float");
  VTKM_TEST_ASSERT(
        (handle.IsTypeAndStorage<vtkm::Float32, VTKM_DEFAULT_STORAGE_TAG>()),
        "Type not right.");
  VTKM_TEST_ASSERT(
        !(handle.IsTypeAndStorage<vtkm::Int32, VTKM_DEFAULT_STORAGE_TAG>()),
        "Type not right.");

  vtkm::Int32 idBuffer[10];
  handle = LoadDynamicArray(idBuffer, 10, "int");
  VTKM_TEST_ASSERT(
        (handle.IsTypeAndStorage<vtkm::Int32, VTKM_DEFAULT_STORAGE_TAG>()),
        "Type not right.");
  VTKM_TEST_ASSERT(
        !(handle.IsTypeAndStorage<vtkm::Float32, VTKM_DEFAULT_STORAGE_TAG>()),
        "Type not right.");
}

void NonTypeQueriesDynamicArrayHandle()
{
  ////
  //// BEGIN-EXAMPLE NonTypeQueriesDynamicArrayHandle.cxx
  ////
  std::vector<vtkm::Float32> scalarBuffer(10);
  vtkm::cont::DynamicArrayHandle scalarDynamicHandle(
        vtkm::cont::make_ArrayHandle(scalarBuffer));

  // This returns 10.
  vtkm::Id scalarArraySize = scalarDynamicHandle.GetNumberOfValues();

  // This returns 1.
  vtkm::IdComponent scalarComponents =
      scalarDynamicHandle.GetNumberOfComponents();
  //// PAUSE-EXAMPLE
  VTKM_TEST_ASSERT(scalarArraySize == 10, "Got wrong array size.");
  VTKM_TEST_ASSERT(scalarComponents == 1, "Got wrong vec size.");
  //// RESUME-EXAMPLE

  std::vector<vtkm::Vec<vtkm::Float32,3> > vectorBuffer(20);
  vtkm::cont::DynamicArrayHandle vectorDynamicHandle(
        vtkm::cont::make_ArrayHandle(vectorBuffer));

  // This returns 20.
  vtkm::Id vectorArraySize = vectorDynamicHandle.GetNumberOfValues();

  // This returns 3.
  vtkm::IdComponent vectorComponents =
      vectorDynamicHandle.GetNumberOfComponents();
  //// PAUSE-EXAMPLE
  VTKM_TEST_ASSERT(vectorArraySize == 20, "Got wrong array size.");
  VTKM_TEST_ASSERT(vectorComponents == 3, "Got wrong vec size.");
  //// RESUME-EXAMPLE
  ////
  //// END-EXAMPLE NonTypeQueriesDynamicArrayHandle.cxx
  ////
}

void DynamicArrayHandleNewInstance()
{
  ////
  //// BEGIN-EXAMPLE DynamicArrayHandleNewInstance.cxx
  ////
  std::vector<vtkm::Float32> scalarBuffer(10);
  vtkm::cont::DynamicArrayHandle dynamicHandle(
        vtkm::cont::make_ArrayHandle(scalarBuffer));

  // This creates a new empty array of type Float32.
  vtkm::cont::DynamicArrayHandle newDynamicArray = dynamicHandle.NewInstance();
  ////
  //// END-EXAMPLE DynamicArrayHandleNewInstance.cxx
  ////

  VTKM_TEST_ASSERT(newDynamicArray.GetNumberOfValues() == 0,
                   "New array not empty.");
  VTKM_TEST_ASSERT((newDynamicArray.IsTypeAndStorage<
                      vtkm::Float32,VTKM_DEFAULT_STORAGE_TAG>()),
                   "New array is wrong type.");
}

void QueryCastDynamicArrayHandle()
{
  ////
  //// BEGIN-EXAMPLE QueryDynamicArrayHandle.cxx
  ////
  std::vector<vtkm::Float32> scalarBuffer(10);
  vtkm::cont::ArrayHandle<vtkm::Float32> concreteHandle =
      vtkm::cont::make_ArrayHandle(scalarBuffer);
  vtkm::cont::DynamicArrayHandle dynamicHandle(concreteHandle);

  // This returns true
  bool isFloat32Array = dynamicHandle.IsSameType(concreteHandle);

  // This returns false
  bool isIdArray =
      dynamicHandle.IsType<vtkm::cont::ArrayHandle<vtkm::Id> >();

  // This returns true
  bool isFloat32 =
      dynamicHandle.IsTypeAndStorage<vtkm::Float32,VTKM_DEFAULT_STORAGE_TAG>();

  // This returns false
  bool isId =
      dynamicHandle.IsTypeAndStorage<vtkm::Id,VTKM_DEFAULT_STORAGE_TAG>();

  // This returns false
  bool isErrorStorage = dynamicHandle.IsTypeAndStorage<
      vtkm::Float32,
      vtkm::cont::ArrayHandleCounting<vtkm::Float32>::StorageTag>();
  ////
  //// END-EXAMPLE QueryDynamicArrayHandle.cxx
  ////

  VTKM_TEST_ASSERT(isFloat32Array, "Didn't query right.");
  VTKM_TEST_ASSERT(!isIdArray, "Didn't query right.");
  VTKM_TEST_ASSERT(isFloat32, "Didn't query right.");
  VTKM_TEST_ASSERT(!isId, "Didn't query right.");
  VTKM_TEST_ASSERT(!isErrorStorage, "Didn't query right.");

  ////
  //// BEGIN-EXAMPLE CastDynamicArrayHandle.cxx
  ////
  dynamicHandle.CopyTo(concreteHandle);
  ////
  //// END-EXAMPLE CastDynamicArrayHandle.cxx
  ////

  VTKM_TEST_ASSERT(concreteHandle.GetNumberOfValues() == 10,
                   "Unexpected length");
}

////
//// BEGIN-EXAMPLE UsingCastAndCall.cxx
////
struct PrintArrayContentsFunctor
{
  template<typename T, typename Storage>
  VTKM_CONT
  void operator()(const vtkm::cont::ArrayHandle<T,Storage> &array) const
  {
    this->PrintArrayPortal(array.GetPortalConstControl());
  }

private:
  template<typename PortalType>
  VTKM_CONT
  void PrintArrayPortal(const PortalType &portal) const
  {
    for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++)
    {
      // All ArrayPortal objects have ValueType for the type of each value.
      using ValueType = typename PortalType::ValueType;

      ValueType value = portal.Get(index);

      vtkm::IdComponent numComponents =
          vtkm::VecTraits<ValueType>::GetNumberOfComponents(value);
      for (vtkm::IdComponent componentIndex = 0;
           componentIndex < numComponents;
           componentIndex++)
      {
        std::cout << " "
                  << vtkm::VecTraits<ValueType>::GetComponent(value,
                                                              componentIndex);
      }
      std::cout << std::endl;
    }
  }
};

template<typename DynamicArrayType>
void PrintArrayContents(const DynamicArrayType &array)
{
  array.CastAndCall(PrintArrayContentsFunctor());
}
////
//// END-EXAMPLE UsingCastAndCall.cxx
////

namespace second_def {

////
//// BEGIN-EXAMPLE DynamicArrayHandleBase.cxx
////
template<typename TypeList, typename StorageList>
void PrintArrayContents(
    const vtkm::cont::DynamicArrayHandleBase<TypeList,StorageList> &array)
{
  array.CastAndCall(PrintArrayContentsFunctor());
}
////
//// END-EXAMPLE DynamicArrayHandleBase.cxx
////

} // namespace second_def

////
//// BEGIN-EXAMPLE CastAndCallStorage.cxx
////
struct MyIdStorageList :
    vtkm::ListTagBase<
        vtkm::cont::StorageTagBasic,
        vtkm::cont::ArrayHandleIndex::StorageTag>
{  };

void PrintIds(vtkm::cont::DynamicArrayHandle array)
{
  PrintArrayContents(array.ResetStorageList(MyIdStorageList()));
}
////
//// END-EXAMPLE CastAndCallStorage.cxx
////

void TryPrintArrayContents()
{
  vtkm::cont::ArrayHandleIndex implicitArray(10);

  vtkm::cont::ArrayHandle<vtkm::Id> concreteArray;
  vtkm::cont::DeviceAdapterAlgorithm<VTKM_DEFAULT_DEVICE_ADAPTER_TAG>::Copy(
        implicitArray, concreteArray);

  vtkm::cont::DynamicArrayHandle dynamicArray = concreteArray;

  second_def::PrintArrayContents(dynamicArray);

  ////
  //// BEGIN-EXAMPLE CastAndCallAllTypes.cxx
  ////
  PrintArrayContents(dynamicArray.ResetTypeList(vtkm::TypeListTagAll()));
  ////
  //// END-EXAMPLE CastAndCallAllTypes.cxx
  ////

  ////
  //// BEGIN-EXAMPLE CastAndCallSingleType.cxx
  ////
  PrintArrayContents(dynamicArray.ResetTypeList(vtkm::TypeListTagId()));
  ////
  //// END-EXAMPLE CastAndCallSingleType.cxx
  ////

  dynamicArray = implicitArray;

  PrintIds(dynamicArray);

  ////
  //// BEGIN-EXAMPLE CastAndCallTypeAndStorage.cxx
  ////
  PrintArrayContents(dynamicArray.
                     ResetTypeList(vtkm::TypeListTagId()).
                     ResetStorageList(MyIdStorageList()));
  ////
  //// END-EXAMPLE CastAndCallTypeAndStorage.cxx
  ////
}

void Test()
{
  TryLoadDynamicArray();
  NonTypeQueriesDynamicArrayHandle();
  DynamicArrayHandleNewInstance();
  QueryCastDynamicArrayHandle();
  TryPrintArrayContents();
}

} // anonymous namespace

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