// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright 2008 Sandia Corporation
// SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov
#include "vtkTreeMapToPolyData.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkFloatArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkTree.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkTreeMapToPolyData);

vtkTreeMapToPolyData::vtkTreeMapToPolyData()
{
  this->SetRectanglesArrayName("area");
  this->SetLevelArrayName("level");
  this->LevelDeltaZ = 0.001;
  this->AddNormals = true;
}

vtkTreeMapToPolyData::~vtkTreeMapToPolyData() = default;

int vtkTreeMapToPolyData::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkTree");
  return 1;
}

int vtkTreeMapToPolyData::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  // get the info objects
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  // get the input and output
  vtkTree* inputTree = vtkTree::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkPolyData* outputPoly = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

  // For each input vertex create 4 points and 1 cell (quad)
  vtkPoints* outputPoints = vtkPoints::New();
  outputPoints->SetNumberOfPoints(inputTree->GetNumberOfVertices() * 4);
  vtkCellArray* outputCells = vtkCellArray::New();

  // Create an array for the point normals
  vtkFloatArray* normals = vtkFloatArray::New();
  normals->SetNumberOfComponents(3);
  normals->SetNumberOfTuples(inputTree->GetNumberOfVertices() * 4);
  normals->SetName("normals");

  vtkDataArray* coordArray = this->GetInputArrayToProcess(0, inputTree);
  if (!coordArray)
  {
    vtkErrorMacro("Area array not found.");
    return 0;
  }
  vtkDataArray* levelArray = this->GetInputArrayToProcess(1, inputTree);

  // Now set the point coordinates, normals, and insert the cell
  for (int i = 0; i < inputTree->GetNumberOfVertices(); i++)
  {
    // Grab coords from the input
    double coords[4];
    coordArray->GetTuple(i, coords);

    double z = 0;
    if (levelArray)
    {
      z = this->LevelDeltaZ * levelArray->GetTuple1(i);
    }
    else
    {
      z = this->LevelDeltaZ * inputTree->GetLevel(i);
    }

    int index = i * 4;
    outputPoints->SetPoint(index, coords[0], coords[2], z);
    outputPoints->SetPoint(index + 1, coords[1], coords[2], z);
    outputPoints->SetPoint(index + 2, coords[1], coords[3], z);
    outputPoints->SetPoint(index + 3, coords[0], coords[3], z);

    // Create an asymmetric gradient on the cells
    // this gradient helps differentiate same colored
    // cells from their neighbors. The asymmetric
    // nature of the gradient is required.
    normals->SetComponent(index, 0, 0);
    normals->SetComponent(index, 1, .707);
    normals->SetComponent(index, 2, .707);

    normals->SetComponent(index + 1, 0, 0);
    normals->SetComponent(index + 1, 1, .866);
    normals->SetComponent(index + 1, 2, .5);

    normals->SetComponent(index + 2, 0, 0);
    normals->SetComponent(index + 2, 1, .707);
    normals->SetComponent(index + 2, 2, .707);

    normals->SetComponent(index + 3, 0, 0);
    normals->SetComponent(index + 3, 1, 0);
    normals->SetComponent(index + 3, 2, 1);

    // Create the cell that uses these points
    vtkIdType cellConn[] = { index, index + 1, index + 2, index + 3 };
    outputCells->InsertNextCell(4, cellConn);
  }

  // Pass the input point data to the output cell data :)
  outputPoly->GetCellData()->PassData(inputTree->GetVertexData());

  // Set the output points and cells
  outputPoly->SetPoints(outputPoints);
  outputPoly->SetPolys(outputCells);

  if (this->AddNormals)
  {
    // Set the point normals
    outputPoly->GetPointData()->AddArray(normals);
    outputPoly->GetPointData()->SetActiveNormals("normals");
  }

  // Clean up.
  normals->Delete();
  outputPoints->Delete();
  outputCells->Delete();

  return 1;
}

void vtkTreeMapToPolyData::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "LevelDeltaZ: " << this->LevelDeltaZ << endl;
  os << indent << "AddNormals: " << this->AddNormals << endl;
}
VTK_ABI_NAMESPACE_END
