// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkTexturedSphereSource.h"

#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkTexturedSphereSource);

// Construct sphere with radius=0.5 and default resolution 8 in both Phi
// and Theta directions.
vtkTexturedSphereSource::vtkTexturedSphereSource(int res)
{
  res = res < 4 ? 4 : res;
  this->Radius = 0.5;
  this->ThetaResolution = res;
  this->PhiResolution = res;
  this->Theta = 0.0;
  this->Phi = 0.0;
  this->OutputPointsPrecision = SINGLE_PRECISION;

  this->SetNumberOfInputPorts(0);
}

int vtkTexturedSphereSource::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
{
  // get the info object
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  // get the output
  vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

  int i, j;
  int numPts;
  int numPolys;
  vtkPoints* newPoints;
  vtkFloatArray* newNormals;
  vtkFloatArray* newTCoords;
  vtkCellArray* newPolys;
  double x[3], deltaPhi, deltaTheta, phi, theta, radius, norm;
  vtkIdType pts[3];
  double tc[2];

  //
  // Set things up; allocate memory
  //

  numPts = (this->PhiResolution + 1) * (this->ThetaResolution + 1);
  // creating triangles
  numPolys = this->PhiResolution * 2 * this->ThetaResolution;

  newPoints = vtkPoints::New();

  // Set the desired precision for the points in the output.
  if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION)
  {
    newPoints->SetDataType(VTK_DOUBLE);
  }
  else
  {
    newPoints->SetDataType(VTK_FLOAT);
  }

  newPoints->Allocate(numPts);
  newNormals = vtkFloatArray::New();
  newNormals->SetNumberOfComponents(3);
  newNormals->Allocate(3 * numPts);
  newTCoords = vtkFloatArray::New();
  newTCoords->SetNumberOfComponents(2);
  newTCoords->Allocate(2 * numPts);
  newPolys = vtkCellArray::New();
  newPolys->AllocateEstimate(numPolys, 3);
  //
  // Create sphere
  //
  // Create intermediate points
  deltaPhi = vtkMath::Pi() / this->PhiResolution;
  deltaTheta = 2.0 * vtkMath::Pi() / this->ThetaResolution;
  for (i = 0; i <= this->ThetaResolution; i++)
  {
    theta = i * deltaTheta;
    tc[0] = theta / (2.0 * vtkMath::Pi());
    for (j = 0; j <= this->PhiResolution; j++)
    {
      phi = j * deltaPhi;
      radius = this->Radius * sin(phi);
      x[0] = radius * cos(theta);
      x[1] = radius * sin(theta);
      x[2] = this->Radius * cos(phi);
      newPoints->InsertNextPoint(x);

      if ((norm = vtkMath::Norm(x)) == 0.0)
      {
        norm = 1.0;
      }
      x[0] /= norm;
      x[1] /= norm;
      x[2] /= norm;
      newNormals->InsertNextTuple(x);

      tc[1] = 1.0 - phi / vtkMath::Pi();
      newTCoords->InsertNextTuple(tc);
    }
  }
  //
  // Generate mesh connectivity
  //
  // bands between poles
  for (i = 0; i < this->ThetaResolution; i++)
  {
    for (j = 0; j < this->PhiResolution; j++)
    {
      pts[0] = (this->PhiResolution + 1) * i + j;
      pts[1] = pts[0] + 1;
      pts[2] = ((this->PhiResolution + 1) * (i + 1) + j) + 1;
      newPolys->InsertNextCell(3, pts);

      pts[1] = pts[2];
      pts[2] = pts[1] - 1;
      newPolys->InsertNextCell(3, pts);
    }
  }
  //
  // Update ourselves and release memory
  //
  output->SetPoints(newPoints);
  newPoints->Delete();

  output->GetPointData()->SetNormals(newNormals);
  newNormals->Delete();

  output->GetPointData()->SetTCoords(newTCoords);
  newTCoords->Delete();

  output->SetPolys(newPolys);
  newPolys->Delete();

  return 1;
}

void vtkTexturedSphereSource::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Theta Resolution: " << this->ThetaResolution << "\n";
  os << indent << "Phi Resolution: " << this->PhiResolution << "\n";
  os << indent << "Theta: " << this->Theta << "\n";
  os << indent << "Phi: " << this->Phi << "\n";
  os << indent << "Radius: " << this->Radius << "\n";
  os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n";
}
VTK_ABI_NAMESPACE_END
