// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkExtractGeometry.h"
#include "vtkImageData.h"
#include "vtkPolyData.h"
#include "vtkSmartPointer.h"
#include "vtkSphere.h"
#include "vtkSphereSource.h"
#include "vtkStaticCellLinks.h"
#include "vtkStaticCellLinksTemplate.h"
#include "vtkTimerLog.h"
#include "vtkUnstructuredGrid.h"

#include <iostream>

// Test the building of static cell links in both unstructured and structured
// grids.
int TestStaticCellLinks(int, char*[])
{
  int dataDim = 3;

  // First create a volume which will be converted to an unstructured grid
  vtkSmartPointer<vtkImageData> volume = vtkSmartPointer<vtkImageData>::New();
  volume->SetDimensions(dataDim, dataDim, dataDim);
  volume->AllocateScalars(VTK_INT, 1);

  //----------------------------------------------------------------------------
  // Build links on volume
  vtkNew<vtkStaticCellLinks> imlinks;
  imlinks->SetDataSet(volume);
  imlinks->BuildLinks();

  vtkIdType ncells = imlinks->GetNumberOfCells(0);
  const vtkIdType* imcells = imlinks->GetCells(0);
  std::cout << "Volume:\n";
  std::cout << "   Lower Left corner (numCells, cells): " << ncells << " (";
  for (int i = 0; i < ncells; ++i)
  {
    std::cout << imcells[i];
    if (i < (ncells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (ncells != 1 || imcells[0] != 0)
  {
    return EXIT_FAILURE;
  }

  ncells = imlinks->GetNumberOfCells(13);
  imcells = imlinks->GetCells(13);
  std::cout << "   Center (ncells, cells): " << ncells << " (";
  for (int i = 0; i < ncells; ++i)
  {
    std::cout << imcells[i];
    if (i < (ncells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (ncells != 8)
  {
    return EXIT_FAILURE;
  }

  ncells = imlinks->GetNumberOfCells(26);
  imcells = imlinks->GetCells(26);
  std::cout << "   Upper Right corner (ncells, cells): " << ncells << " (";
  for (int i = 0; i < ncells; ++i)
  {
    std::cout << imcells[i];
    if (i < (ncells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (ncells != 1 || imcells[0] != 7)
  {
    return EXIT_FAILURE;
  }

  //----------------------------------------------------------------------------
  // Unstructured grid
  vtkSmartPointer<vtkSphere> sphere = vtkSmartPointer<vtkSphere>::New();
  sphere->SetCenter(0, 0, 0);
  sphere->SetRadius(100000);

  // Side effect of this filter is conversion of volume to unstructured grid
  vtkSmartPointer<vtkExtractGeometry> extract = vtkSmartPointer<vtkExtractGeometry>::New();
  extract->SetInputData(volume);
  extract->SetImplicitFunction(sphere);
  extract->Update();

  // Grab the output, build links on unstructured grid
  vtkSmartPointer<vtkUnstructuredGrid> ugrid = extract->GetOutput();

  vtkStaticCellLinksTemplate<int> slinks;
  slinks.BuildLinks(ugrid);

  int numCells = slinks.GetNumberOfCells(0);
  const int* cells = slinks.GetCells(0);
  std::cout << "\nUnstructured Grid:\n";
  std::cout << "   Lower Left corner (numCells, cells): " << numCells << " (";
  for (int i = 0; i < numCells; ++i)
  {
    std::cout << cells[i];
    if (i < (numCells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (numCells != 1 || cells[0] != 0)
  {
    return EXIT_FAILURE;
  }

  numCells = slinks.GetNumberOfCells(13);
  cells = slinks.GetCells(13);
  std::cout << "   Center (numCells, cells): " << numCells << " (";
  for (int i = 0; i < numCells; ++i)
  {
    std::cout << cells[i];
    if (i < (numCells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (numCells != 8)
  {
    return EXIT_FAILURE;
  }

  numCells = slinks.GetNumberOfCells(26);
  cells = slinks.GetCells(26);
  std::cout << "   Upper Right corner (numCells, cells): " << numCells << " (";
  for (int i = 0; i < numCells; ++i)
  {
    std::cout << cells[i];
    if (i < (numCells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (numCells != 1 || cells[0] != 7)
  {
    return EXIT_FAILURE;
  }

  //----------------------------------------------------------------------------
  // Polydata
  vtkSmartPointer<vtkSphereSource> ss = vtkSmartPointer<vtkSphereSource>::New();
  ss->SetThetaResolution(12);
  ss->SetPhiResolution(10);
  ss->Update();

  vtkSmartPointer<vtkPolyData> pdata = ss->GetOutput();

  slinks.Initialize(); // reuse
  slinks.BuildLinks(pdata);

  // The first point is at the pole
  numCells = slinks.GetNumberOfCells(0);
  cells = slinks.GetCells(0);
  std::cout << "\nPolydata:\n";
  std::cout << "   Pole: (numCells, cells): " << numCells << " (";
  for (int i = 0; i < numCells; ++i)
  {
    std::cout << cells[i];
    if (i < (numCells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (numCells != 12)
  {
    return EXIT_FAILURE;
  }

  // The next point is at near the equator
  numCells = slinks.GetNumberOfCells(5);
  cells = slinks.GetCells(5);
  std::cout << "   Equator: (numCells, cells): " << numCells << " (";
  for (int i = 0; i < numCells; ++i)
  {
    std::cout << cells[i];
    if (i < (numCells - 1))
      std::cout << ",";
  }
  std::cout << ")\n";
  if (numCells != 6)
  {
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
