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

#include "vtkCellArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkOutlineSource);

//------------------------------------------------------------------------------
vtkOutlineSource::vtkOutlineSource()
{
  this->BoxType = VTK_BOX_TYPE_AXIS_ALIGNED;

  this->GenerateFaces = 0;

  this->OutputPointsPrecision = SINGLE_PRECISION;

  for (int i = 0; i < 3; i++)
  {
    this->Bounds[2 * i] = -1.0;
    this->Bounds[2 * i + 1] = 1.0;
  }

  // Sensible initial values
  this->Corners[0] = 0.0;
  this->Corners[1] = 0.0;
  this->Corners[2] = 0.0;
  this->Corners[3] = 1.0;
  this->Corners[4] = 0.0;
  this->Corners[5] = 0.0;
  this->Corners[6] = 0.0;
  this->Corners[7] = 1.0;
  this->Corners[8] = 0.0;
  this->Corners[9] = 1.0;
  this->Corners[10] = 1.0;
  this->Corners[11] = 0.0;
  this->Corners[12] = 0.0;
  this->Corners[13] = 0.0;
  this->Corners[14] = 1.0;
  this->Corners[15] = 1.0;
  this->Corners[16] = 0.0;
  this->Corners[17] = 1.0;
  this->Corners[18] = 0.0;
  this->Corners[19] = 1.0;
  this->Corners[20] = 1.0;
  this->Corners[21] = 1.0;
  this->Corners[22] = 1.0;
  this->Corners[23] = 1.0;

  this->SetNumberOfInputPorts(0);
}

//------------------------------------------------------------------------------
int vtkOutlineSource::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()));

  double bounds[6];
  double x[3];
  vtkIdType pts[4];
  vtkPoints* newPts;
  vtkCellArray* newLines;
  vtkCellArray* newPolys = nullptr;

  //
  // Initialize
  //
  for (int i = 0; i < 6; i += 2)
  {
    int j = i + 1;
    bounds[i] = this->Bounds[i];
    bounds[j] = this->Bounds[j];
    if (bounds[i] > bounds[j])
    {
      double tmp = bounds[i];
      bounds[i] = bounds[j];
      bounds[j] = tmp;
    }
  }

  //
  // Allocate storage and create outline
  //
  newPts = vtkPoints::New();

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

  newPts->Allocate(8);
  newLines = vtkCellArray::New();
  newLines->AllocateEstimate(12, 2);

  if (this->GenerateFaces)
  {
    newPolys = vtkCellArray::New();
    newPolys->AllocateEstimate(6, 4);
  }

  if (this->BoxType == VTK_BOX_TYPE_AXIS_ALIGNED)
  {
    x[0] = bounds[0];
    x[1] = bounds[2];
    x[2] = bounds[4];
    newPts->InsertPoint(0, x);
    x[0] = bounds[1];
    x[1] = bounds[2];
    x[2] = bounds[4];
    newPts->InsertPoint(1, x);
    x[0] = bounds[0];
    x[1] = bounds[3];
    x[2] = bounds[4];
    newPts->InsertPoint(2, x);
    x[0] = bounds[1];
    x[1] = bounds[3];
    x[2] = bounds[4];
    newPts->InsertPoint(3, x);
    x[0] = bounds[0];
    x[1] = bounds[2];
    x[2] = bounds[5];
    newPts->InsertPoint(4, x);
    x[0] = bounds[1];
    x[1] = bounds[2];
    x[2] = bounds[5];
    newPts->InsertPoint(5, x);
    x[0] = bounds[0];
    x[1] = bounds[3];
    x[2] = bounds[5];
    newPts->InsertPoint(6, x);
    x[0] = bounds[1];
    x[1] = bounds[3];
    x[2] = bounds[5];
    newPts->InsertPoint(7, x);
  }
  else // VTK_BOX_TYPE_ORIENTED
  {
    newPts->InsertPoint(0, &Corners[0]);
    newPts->InsertPoint(1, &Corners[3]);
    newPts->InsertPoint(2, &Corners[6]);
    newPts->InsertPoint(3, &Corners[9]);
    newPts->InsertPoint(4, &Corners[12]);
    newPts->InsertPoint(5, &Corners[15]);
    newPts->InsertPoint(6, &Corners[18]);
    newPts->InsertPoint(7, &Corners[21]);
  }

  pts[0] = 0;
  pts[1] = 1;
  newLines->InsertNextCell(2, pts);
  pts[0] = 2;
  pts[1] = 3;
  newLines->InsertNextCell(2, pts);
  pts[0] = 4;
  pts[1] = 5;
  newLines->InsertNextCell(2, pts);
  pts[0] = 6;
  pts[1] = 7;
  newLines->InsertNextCell(2, pts);
  pts[0] = 0;
  pts[1] = 2;
  newLines->InsertNextCell(2, pts);
  pts[0] = 1;
  pts[1] = 3;
  newLines->InsertNextCell(2, pts);
  pts[0] = 4;
  pts[1] = 6;
  newLines->InsertNextCell(2, pts);
  pts[0] = 5;
  pts[1] = 7;
  newLines->InsertNextCell(2, pts);
  pts[0] = 0;
  pts[1] = 4;
  newLines->InsertNextCell(2, pts);
  pts[0] = 1;
  pts[1] = 5;
  newLines->InsertNextCell(2, pts);
  pts[0] = 2;
  pts[1] = 6;
  newLines->InsertNextCell(2, pts);
  pts[0] = 3;
  pts[1] = 7;
  newLines->InsertNextCell(2, pts);

  if (newPolys)
  {
    pts[0] = 1;
    pts[1] = 0;
    pts[2] = 2;
    pts[3] = 3;
    newPolys->InsertNextCell(4, pts);
    pts[0] = 0;
    pts[1] = 1;
    pts[2] = 5;
    pts[3] = 4;
    newPolys->InsertNextCell(4, pts);
    pts[0] = 2;
    pts[1] = 0;
    pts[2] = 4;
    pts[3] = 6;
    newPolys->InsertNextCell(4, pts);
    pts[0] = 3;
    pts[1] = 2;
    pts[2] = 6;
    pts[3] = 7;
    newPolys->InsertNextCell(4, pts);
    pts[0] = 1;
    pts[1] = 3;
    pts[2] = 7;
    pts[3] = 5;
    newPolys->InsertNextCell(4, pts);
    pts[0] = 7;
    pts[1] = 6;
    pts[2] = 4;
    pts[3] = 5;
    newPolys->InsertNextCell(4, pts);
  }

  // Update selves and release memory
  //
  output->SetPoints(newPts);
  newPts->Delete();

  output->SetLines(newLines);
  newLines->Delete();

  if (newPolys)
  {
    output->SetPolys(newPolys);
    newPolys->Delete();
  }

  return 1;
}

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

  os << indent << "Generate Faces: " << (this->GenerateFaces ? "On\n" : "Off\n");

  os << indent << "Box Type: ";
  if (this->BoxType == VTK_BOX_TYPE_AXIS_ALIGNED)
  {
    os << "Axis Aligned\n";
    os << indent << "Bounds: "
       << "(" << this->Bounds[0] << ", " << this->Bounds[1] << ") "
       << "(" << this->Bounds[2] << ", " << this->Bounds[3] << ") "
       << "(" << this->Bounds[4] << ", " << this->Bounds[5] << ")\n";
  }
  else
  {
    os << "Corners: (\n";
    for (int i = 0; i < 8; i++)
    {
      os << "\t" << this->Corners[3 * i] << ", " << this->Corners[3 * i + 1] << ", "
         << this->Corners[3 * i + 2] << "\n";
    }
    os << ")\n";
  }

  os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n";
}
VTK_ABI_NAMESPACE_END
