////
//// BEGIN-EXAMPLE CustomTypeLists.cxx
////
#define VTKM_DEFAULT_TYPE_LIST_TAG MyCommonTypes

#include <vtkm/ListTag.h>
#include <vtkm/TypeListTag.h>
//// PAUSE-EXAMPLE
namespace
{
//// RESUME-EXAMPLE

// A list of 2D vector types.
struct Vec2List
  : vtkm::ListTagBase<vtkm::Id2,
                      vtkm::Vec<vtkm::Float32, 2>,
                      vtkm::Vec<vtkm::Float64, 2>>
{
};

// An application that uses 2D geometry might commonly encounter this list of
// types.
struct MyCommonTypes : vtkm::ListTagJoin<Vec2List, vtkm::TypeListTagCommon>
{
};
////
//// END-EXAMPLE CustomTypeLists.cxx
////

} // anonymous namespace

#include <vtkm/VecTraits.h>

#include <vtkm/testing/Testing.h>

#include <algorithm>
#include <string>
#include <vector>

////
//// BEGIN-EXAMPLE BaseListTags.cxx
////
#include <vtkm/ListTag.h>
//// PAUSE-EXAMPLE
namespace
{
//// RESUME-EXAMPLE

// Placeholder classes representing things that might be in a template
// metaprogram list.
class Foo;
class Bar;
class Baz;
class Qux;
class Xyzzy;

// The names of the following tags are indicative of the lists they contain.

struct FooList : vtkm::ListTagBase<Foo>
{
};

struct FooBarList : vtkm::ListTagBase<Foo, Bar>
{
};

struct BazQuxXyzzyList : vtkm::ListTagBase<Baz, Qux, Xyzzy>
{
};

struct QuxBazBarFooList : vtkm::ListTagBase<Qux, Baz, Bar, Foo>
{
};

struct FooBarBazQuxXyzzyList : vtkm::ListTagJoin<FooBarList, BazQuxXyzzyList>
{
};
////
//// END-EXAMPLE BaseListTags.cxx
////

class Foo
{
};
class Bar
{
};
class Baz
{
};
class Qux
{
};
class Xyzzy
{
};

struct ListTagsFunctor
{
  std::string FoundTags;

  template<typename T>
  void operator()(T)
  {
    this->FoundTags.append(vtkm::testing::TypeName<T>::Name());
  }

  void operator()(Foo) { this->FoundTags.append("Foo"); }
  void operator()(Bar) { this->FoundTags.append("Bar"); }
  void operator()(Baz) { this->FoundTags.append("Baz"); }
  void operator()(Qux) { this->FoundTags.append("Qux"); }
  void operator()(Xyzzy) { this->FoundTags.append("Xyzzy"); }
};

template<typename ListTag>
void TryListTag(ListTag, const char* expectedString)
{
  ListTagsFunctor checkFunctor;
  vtkm::ListForEach(checkFunctor, ListTag());
  std::cout << std::endl
            << "Expected " << expectedString << std::endl
            << "Found    " << checkFunctor.FoundTags << std::endl;
  VTKM_TEST_ASSERT(checkFunctor.FoundTags == expectedString, "List wrong");
}

void TestBaseListTags()
{
  TryListTag(FooList(), "Foo");
  TryListTag(FooBarList(), "FooBar");
  TryListTag(BazQuxXyzzyList(), "BazQuxXyzzy");
  TryListTag(QuxBazBarFooList(), "QuxBazBarFoo");
  TryListTag(FooBarBazQuxXyzzyList(), "FooBarBazQuxXyzzy");
}

void TestCustomTypeLists()
{
#ifdef VTKM_USE_64BIT_IDS
  TryListTag(Vec2List(),
             "vtkm::Vec< vtkm::Int64, 2 >vtkm::Vec< vtkm::Float32, 2 "
             ">vtkm::Vec< vtkm::Float64, 2 >");
  TryListTag(
    MyCommonTypes(),
    "vtkm::Vec< vtkm::Int64, 2 >vtkm::Vec< vtkm::Float32, 2 >vtkm::Vec< "
    "vtkm::Float64, 2 >vtkm::Int32vtkm::Int64vtkm::Float32vtkm::Float64vtkm::Vec< "
    "vtkm::Float32, 3 >vtkm::Vec< vtkm::Float64, 3 >");
#else
  TryListTag(Vec2List(),
             "vtkm::Vec< vtkm::Int32, 2 >vtkm::Vec< vtkm::Float32, 2 "
             ">vtkm::Vec< vtkm::Float64, 2 >");
  TryListTag(
    MyCommonTypes(),
    "vtkm::Vec< vtkm::Int32, 2 >vtkm::Vec< vtkm::Float32, 2 >vtkm::Vec< "
    "vtkm::Float64, 2 >vtkm::Int32vtkm::Int64vtkm::Float32vtkm::Float64vtkm::Vec< "
    "vtkm::Float32, 3 >vtkm::Vec< vtkm::Float64, 3 >");
#endif
}

////
//// BEGIN-EXAMPLE ListForEach.cxx
////
struct MyArrayBase
{
  // A virtual destructor makes sure C++ RTTI will be generated. It also helps
  // ensure subclass destructors are called.
  virtual ~MyArrayBase() {}
};

template<typename T>
struct MyArrayImpl : public MyArrayBase
{
  std::vector<T> Array;
};

template<typename T>
void PrefixSum(std::vector<T>& array)
{
  T sum(typename vtkm::VecTraits<T>::ComponentType(0));
  for (typename std::vector<T>::iterator iter = array.begin(); iter != array.end();
       iter++)
  {
    sum = sum + *iter;
    *iter = sum;
  }
}

struct PrefixSumFunctor
{
  MyArrayBase* ArrayPointer;

  PrefixSumFunctor(MyArrayBase* arrayPointer)
    : ArrayPointer(arrayPointer)
  {
  }

  template<typename T>
  void operator()(T)
  {
    using ConcreteArrayType = MyArrayImpl<T>;
    ConcreteArrayType* concreteArray =
      dynamic_cast<ConcreteArrayType*>(this->ArrayPointer);
    if (concreteArray != NULL)
    {
      PrefixSum(concreteArray->Array);
    }
  }
};

void DoPrefixSum(MyArrayBase* array)
{
  PrefixSumFunctor functor = PrefixSumFunctor(array);
  vtkm::ListForEach(functor, vtkm::TypeListTagCommon());
}
////
//// END-EXAMPLE ListForEach.cxx
////

void TestPrefixSum()
{
  MyArrayImpl<vtkm::Id> array;
  array.Array.resize(10);
  std::fill(array.Array.begin(), array.Array.end(), 1);
  DoPrefixSum(&array);
  for (vtkm::Id index = 0; index < 10; index++)
  {
    VTKM_TEST_ASSERT(array.Array[index] == index + 1, "Got bad prefix sum.");
  }
}

void Test()
{
  TestBaseListTags();
  TestCustomTypeLists();
  TestPrefixSum();
}

} // anonymous namespace

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