// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-License-Identifier: BSD-3-Clause

#ifndef vtkGeometryRepresentationInternal_h
#define vtkGeometryRepresentationInternal_h

#include "vtkInformation.h"       // for vtkInformation
#include "vtkInformationVector.h" // for vtkInformationVector
#include "vtkPolyData.h"          // for vtkPolyData

// We'll use the VTKm decimation filter if TBB is enabled, otherwise we'll
// fallback to vtkQuadricClustering, since vtkmLevelOfDetail is slow on the
// serial backend.
#if VTK_MODULE_ENABLE_VTK_AcceleratorsVTKmFilters
#include "vtkmConfigFilters.h" // for VISKORES_ENABLE_TBB
#endif

#if defined(VISKORES_ENABLE_TBB) && VTK_MODULE_ENABLE_VTK_AcceleratorsVTKmFilters
#include "vtkCellArray.h"         // for vtkCellArray
#include "vtkQuadricClustering.h" // for vtkQuadricClustering
#include "vtkmLevelOfDetail.h"
namespace vtkGeometryRepresentation_detail
{
class DecimationFilterType : public vtkmLevelOfDetail
{
public:
  static DecimationFilterType* New();
  vtkTypeMacro(DecimationFilterType, vtkmLevelOfDetail);

  // See note on the vtkQuadricClustering implementation below.
  void SetLODFactor(double factor)
  {
    factor = vtkMath::ClampValue(factor, 0., 1.);

    // This produces the following number of divisions for 'factor':
    // 0.0 --> 64
    // 0.5 --> 256 (default)
    // 1.0 --> 1024
    int divs = static_cast<int>(std::pow(2, 4. * factor + 6.));
    this->SetNumberOfDivisions(divs, divs, divs);
  }

protected:
  DecimationFilterType()
  {
    this->Fallback->SetUseInputPoints(1);
    this->Fallback->SetCopyCellData(1);
    this->Fallback->SetUseInternalTriangles(0);
  }

  int RequestData(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector) override
  {
    // The accelerated implementation only supports triangle meshes. Fallback to
    // vtkQuadricClustering if needed:
    vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
    vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
    if (!input)
    {
      vtkErrorMacro("Expected polydata.");
      return 0;
    }

    // This filter only handles triangles. This code to detect all triangles is
    // adapted from tovtkm::Convert for vtkPolyData:
    vtkCellArray* polys = input->GetPolys();
    const int maxPolySize = polys->GetMaxCellSize();
    const vtkIdType numCells = input->GetNumberOfCells();
    // deduce if we only have triangles. We use maxCellSize+1 so
    // that we handle the length entry in the cell array for each cell
    polys->Squeeze();
    const bool allSameType = polys->GetNumberOfCells() == numCells &&
      polys->GetNumberOfConnectivityIds() == numCells * maxPolySize;
    if (allSameType && maxPolySize == 3)
    { // All triangles. Try the accelerated implementation:
      if (this->Superclass::RequestData(request, inputVector, outputVector))
      {
        return 1;
      }
    }

    // Otherwise fallback to quadric clustering:
    vtkInformation* outInfo = outputVector->GetInformationObject(0);
    vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

    this->Fallback->SetInputData(input);
    this->Fallback->Update();
    output->ShallowCopy(this->Fallback->GetOutput(0));

    return 1;
  }

  vtkNew<vtkQuadricClustering> Fallback;
};
}
#else // VISKORES_ENABLE_TBB
#include "vtkQuadricClustering.h"
namespace vtkGeometryRepresentation_detail
{
class DecimationFilterType : public vtkQuadricClustering
{
public:
  static DecimationFilterType* New();
  vtkTypeMacro(DecimationFilterType, vtkQuadricClustering);

  // This version gets slower as the grid increases, while the VISKORES version
  // scales with number of points. This means we can get away with a much finer
  // grid with the VISKORES filter, so we'll just reduce the mesh quality a bit
  // here.
  void SetLODFactor(double factor)
  {
    factor = vtkMath::ClampValue(factor, 0., 1.);

    // This is the same equation used in the old implementation:
    // 0.0 --> 10
    // 0.5 --> 85 (default)
    // 1.0 --> 160
    int divs = static_cast<int>(150 * factor) + 10;
    this->SetNumberOfDivisions(divs, divs, divs);
  }

protected:
  DecimationFilterType()
  {
    this->SetUseInputPoints(1);
    this->SetCopyCellData(1);
    this->SetUseInternalTriangles(0);
  }
};
}
#endif // VISKORES_ENABLE_TBB

#endif

// VTK-HeaderTest-Exclude: vtkGeometryRepresentationInternal.h
