// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkDataSetCellIterator.h"

#include "vtkHyperTreeGrid.h"
#include "vtkIdList.h"
#include "vtkObjectFactory.h"
#include "vtkPoints.h"
#include "vtkRectilinearGrid.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkDataSetCellIterator);

namespace
{
template <typename T>
void SetArrayType(T* grid, vtkPoints* points)
{
  // check all directions to see if any of them are doubles and
  // if they are we set the points data type to double. If the
  // data types are all the same then we set it to the common
  // data type. Otherwise we give up and just keep the default
  // float data type.
  int xType = -1, yType = -1, zType = -1;
  if (vtkDataArray* x = grid->GetXCoordinates())
  {
    xType = x->GetDataType();
    if (xType == VTK_DOUBLE)
    {
      points->SetDataType(VTK_DOUBLE);
      return;
    }
  }
  if (vtkDataArray* y = grid->GetYCoordinates())
  {
    yType = y->GetDataType();
    if (yType == VTK_DOUBLE)
    {
      points->SetDataType(VTK_DOUBLE);
      return;
    }
  }
  if (vtkDataArray* z = grid->GetZCoordinates())
  {
    zType = z->GetDataType();
    if (zType == VTK_DOUBLE)
    {
      points->SetDataType(VTK_DOUBLE);
      return;
    }
  }
  if (xType != -1 || yType != -1 || zType != -1)
  {
    if (xType == yType && xType == zType)
    {
      points->SetDataType(xType);
      return;
    }
    if (xType == -1)
    {
      if (yType == -1)
      {
        points->SetDataType(zType);
        return;
      }
      else if (zType == -1 || yType == zType)
      {
        points->SetDataType(yType);
        return;
      }
    }
    if (yType == -1)
    {
      if (xType == -1)
      {
        points->SetDataType(zType);
        return;
      }
      else if (zType == -1 || xType == zType)
      {
        points->SetDataType(xType);
        return;
      }
    }
    if (zType == -1)
    {
      if (xType == -1)
      {
        points->SetDataType(yType);
        return;
      }
      else if (yType == -1 || xType == yType)
      {
        points->SetDataType(xType);
        return;
      }
    }
  }

  // Set it to the default since it may have gotten set to something else
  points->SetDataType(VTK_FLOAT);
}
}

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "DataSet: " << this->DataSet << endl;
}

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::SetDataSet(vtkDataSet* ds)
{
  this->DataSet = ds;
  this->CellId = 0;

  if (vtkRectilinearGrid* rg = vtkRectilinearGrid::SafeDownCast(ds))
  {
    SetArrayType(rg, this->Points);
  }
  else if (vtkHyperTreeGrid* htg = vtkHyperTreeGrid::SafeDownCast(ds))
  {
    SetArrayType(htg, this->Points);
  }
  else if (ds->IsA("vtkImageData"))
  {
    // ImageData Origin and Spacing are doubles so
    // the data type for this should also be double
    this->Points->SetDataType(VTK_DOUBLE);
  }
}

//------------------------------------------------------------------------------
bool vtkDataSetCellIterator::IsDoneWithTraversal()
{
  return this->DataSet == nullptr || this->CellId >= this->DataSet->GetNumberOfCells();
}

//------------------------------------------------------------------------------
vtkIdType vtkDataSetCellIterator::GetCellId()
{
  return this->CellId;
}

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::IncrementToNextCell()
{
  ++this->CellId;
}

//------------------------------------------------------------------------------
vtkDataSetCellIterator::vtkDataSetCellIterator()
  : DataSet(nullptr)
  , CellId(0)
{
}

//------------------------------------------------------------------------------
vtkDataSetCellIterator::~vtkDataSetCellIterator() = default;

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::ResetToFirstCell()
{
  this->CellId = 0;
}

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::FetchCellType()
{
  this->CellType = this->DataSet->GetCellType(this->CellId);
}

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::FetchPointIds()
{
  this->DataSet->GetCellPoints(this->CellId, this->PointIds);
}

//------------------------------------------------------------------------------
void vtkDataSetCellIterator::FetchPoints()
{
  // This will fetch the point ids if needed:
  vtkIdList* pointIds = this->GetPointIds();

  vtkIdType numPoints = pointIds->GetNumberOfIds();
  vtkIdType* id = pointIds->GetPointer(0);

  this->Points->SetNumberOfPoints(numPoints);

  double point[3];
  for (int i = 0; i < numPoints; ++i)
  {
    this->DataSet->GetPoint(*id++, point);
    this->Points->SetPoint(i, point);
  }
}
VTK_ABI_NAMESPACE_END
