//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//============================================================================
#include <vtkm/filter/geometry_refinement/Dual.h>

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

using vtkm::cont::testing::MakeTestDataSet;

namespace
{


void TestWithExplicitData()
{
  // Setup input dataset
  std::vector<vtkm::Vec3f> coords{
    vtkm::Vec3f{ 0.0f, 0.0f, 0.0f }, vtkm::Vec3f{ 0.0f, 2.0f, 0.0f },
    vtkm::Vec3f{ 2.0f, 2.0f, 0.0f }, vtkm::Vec3f{ 2.0f, 0.0f, 0.0f },
    vtkm::Vec3f{ 4.0f, 1.0f, 0.0f }, vtkm::Vec3f{ 6.0f, 3.0f, 0.0f },
  };
  std::vector<vtkm::UInt8> shapes{ vtkm::CELL_SHAPE_QUAD,
                                   vtkm::CELL_SHAPE_TRIANGLE,
                                   vtkm::CELL_SHAPE_TRIANGLE };
  std::vector<vtkm::IdComponent> offsets{ 4, 3, 3 };
  std::vector<vtkm::Id> connectivity{ 0, 1, 2, 3, 1, 2, 4, 2, 4, 5 };

  vtkm::cont::DataSetBuilderExplicit dsbe;
  vtkm::cont::DataSet dataset = dsbe.Create(coords, shapes, offsets, connectivity);

  vtkm::cont::ArrayHandle<vtkm::FloatDefault> pointValues =
    vtkm::cont::make_ArrayHandle({ -1.0f, 3.0f, 2.0f, 0.0f, 5.0f, 7.0f });
  dataset.AddField(
    vtkm::cont::Field("pointVar", vtkm::cont::Field::Association::Points, pointValues));

  vtkm::cont::ArrayHandle<vtkm::FloatDefault> cellValues =
    vtkm::cont::make_ArrayHandle({ 1.0f, 5.0f, 2.0f });
  dataset.AddField(vtkm::cont::Field("cellVar", vtkm::cont::Field::Association::Cells, cellValues));

  // Apply the filter
  vtkm::filter::geometry_refinement::Dual dualFilter;
  vtkm::cont::DataSet output = dualFilter.Execute(dataset);
  vtkm::cont::CellSetExplicit<> cellSet =
    output.GetCellSet().AsCellSet<vtkm::cont::CellSetExplicit<>>();

  // Define expected output data
  auto expectedCoords = vtkm::cont::make_ArrayHandle<vtkm::Vec3f>({
    vtkm::Vec3f{ 1.0f, 1.0f, 0.0f },
    vtkm::Vec3f{ 2.6666666666f, 2.0f, 0.0f },
    vtkm::Vec3f{ 4.0f, 2.0f, 0.0f },
  });
  auto expectedShapes = vtkm::cont::make_ArrayHandle<vtkm::UInt8>({
    vtkm::CELL_SHAPE_VERTEX,
    vtkm::CELL_SHAPE_LINE,
    vtkm::CELL_SHAPE_TRIANGLE,
    vtkm::CELL_SHAPE_VERTEX,
    vtkm::CELL_SHAPE_LINE,
    vtkm::CELL_SHAPE_VERTEX,
  });
  auto expectedOffsets = vtkm::cont::make_ArrayHandle<vtkm::IdComponent>({ 1, 2, 3, 1, 2, 1 });
  auto expectedConnectivity =
    vtkm::cont::make_ArrayHandle<vtkm::Id>({ 1, 2, 2, 1, 2, 3, 1, 2, 3, 3 });

  // Assert equality between expected data and filter output
  VTKM_TEST_ASSERT(
    test_equal_ArrayHandles(
      output.GetCoordinateSystem().GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Vec3f>>(),
      expectedCoords),
    "Wrong coordinates");
  VTKM_TEST_ASSERT(test_equal_ArrayHandles(cellSet.GetShapesArray(vtkm::TopologyElementTagCell{},
                                                                  vtkm::TopologyElementTagPoint{}),
                                           expectedShapes),
                   "Wrong shapes");
  VTKM_TEST_ASSERT(test_equal_ArrayHandles(cellSet.GetOffsetsArray(vtkm::TopologyElementTagCell{},
                                                                   vtkm::TopologyElementTagPoint{}),
                                           expectedOffsets),
                   "Wrong offsets");
  VTKM_TEST_ASSERT(
    test_equal_ArrayHandles(
      cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{}),
      expectedConnectivity),
    "Wrong connectivity");
  VTKM_TEST_ASSERT(test_equal_ArrayHandles(output.GetField("pointVar").GetData(), cellValues),
                   "Wrong point data");
  VTKM_TEST_ASSERT(test_equal_ArrayHandles(output.GetField("cellVar").GetData(), pointValues),
                   "Wrong cell data");
}



void TestWith2DStructuredData() {}


void TestDualFilter()
{
  TestWithExplicitData();
  TestWith2DStructuredData();
}

} // anonymous namespace

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