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

#include "vtkBitArray.h"
#include "vtkCommand.h"
#include "vtkDataSetAttributes.h"
#include "vtkHyperTreeGrid.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkUnstructuredGrid.h"

//------------------------------------------------------------------------------
VTK_ABI_NAMESPACE_BEGIN
vtkHyperTreeGridAlgorithm::vtkHyperTreeGridAlgorithm()
{
  // By default, only one input and one output ports
  this->SetNumberOfInputPorts(1);
  this->SetNumberOfOutputPorts(1);

  // Keep references to input and output data
  this->InData = nullptr; // todo: should be a safer pointer type
  this->OutData = nullptr;

  this->AppropriateOutput = false;
}

//------------------------------------------------------------------------------
vtkHyperTreeGridAlgorithm::~vtkHyperTreeGridAlgorithm()
{
  this->InData = nullptr;
  this->OutData = nullptr;
}

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

  if (this->InData)
  {
    os << indent << "InData:\n";
    this->InData->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "InData: ( none )\n";
  }

  os << indent << "OutData: ";
  if (this->OutData)
  {
    this->OutData->PrintSelf(os, indent);
  }
  else
  {
    os << "(none)" << endl;
  }
}

//------------------------------------------------------------------------------
vtkDataObject* vtkHyperTreeGridAlgorithm::GetOutput()
{
  return this->GetOutput(0);
}

//------------------------------------------------------------------------------
vtkDataObject* vtkHyperTreeGridAlgorithm::GetOutput(int port)
{
  return this->GetOutputDataObject(port);
}

//------------------------------------------------------------------------------
vtkHyperTreeGrid* vtkHyperTreeGridAlgorithm::GetHyperTreeGridOutput()
{
  return this->GetHyperTreeGridOutput(0);
}

//------------------------------------------------------------------------------
vtkHyperTreeGrid* vtkHyperTreeGridAlgorithm::GetHyperTreeGridOutput(int port)
{
  return vtkHyperTreeGrid::SafeDownCast(this->GetOutputDataObject(port));
}

//------------------------------------------------------------------------------
vtkPolyData* vtkHyperTreeGridAlgorithm::GetPolyDataOutput()
{
  return this->GetPolyDataOutput(0);
}

//------------------------------------------------------------------------------
vtkPolyData* vtkHyperTreeGridAlgorithm::GetPolyDataOutput(int port)
{
  return vtkPolyData::SafeDownCast(this->GetOutputDataObject(port));
}

//------------------------------------------------------------------------------
vtkUnstructuredGrid* vtkHyperTreeGridAlgorithm::GetUnstructuredGridOutput()
{
  return this->GetUnstructuredGridOutput(0);
}

//------------------------------------------------------------------------------
vtkUnstructuredGrid* vtkHyperTreeGridAlgorithm::GetUnstructuredGridOutput(int port)
{
  return vtkUnstructuredGrid::SafeDownCast(this->GetOutputDataObject(port));
}

//------------------------------------------------------------------------------
void vtkHyperTreeGridAlgorithm::SetOutput(vtkDataObject* d)
{
  this->GetExecutive()->SetOutputData(0, d);
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::RequestDataObject(
  vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  if (this->GetNumberOfInputPorts() == 0 || this->GetNumberOfOutputPorts() == 0)
  {
    return 1;
  }

  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  if (!inInfo)
  {
    return 0;
  }
  vtkDataObject* input = inInfo->Get(vtkDataObject::DATA_OBJECT());

  if (input)
  {
    // for each output
    for (int i = 0; i < this->GetNumberOfOutputPorts(); ++i)
    {
      vtkInformation* info = outputVector->GetInformationObject(i);
      vtkDataObject* output = info->Get(vtkDataObject::DATA_OBJECT());

      if (!output || !output->IsA(input->GetClassName()))
      {
        vtkDataObject* newOutput = input->NewInstance();
        info->Set(vtkDataObject::DATA_OBJECT(), newOutput);
        newOutput->Delete();
      }
    }
  }
  return 1;
}

//------------------------------------------------------------------------------
vtkTypeBool vtkHyperTreeGridAlgorithm::ProcessRequest(
  vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  if (this->AppropriateOutput)
  {
    // create the output
    if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA_OBJECT()))
    {
      return this->RequestDataObject(request, inputVector, outputVector);
    }
  }

  // generate the data
  if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA()))
  {
    return this->RequestData(request, inputVector, outputVector);
  }

  if (request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_UPDATE_EXTENT()))
  {
    return this->RequestUpdateExtent(request, inputVector, outputVector);
  }

  if (request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_UPDATE_TIME()))
  {
    return this->RequestUpdateTime(request, inputVector, outputVector);
  }

  // execute information
  if (request->Has(vtkDemandDrivenPipeline::REQUEST_INFORMATION()))
  {
    return this->RequestInformation(request, inputVector, outputVector);
  }

  return this->Superclass::ProcessRequest(request, inputVector, outputVector);
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::FillInputPortInformation(int, vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkHyperTreeGrid");
  return 1;
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::FillOutputPortInformation(int, vtkInformation* info)
{
  info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkDataObject");
  return 1;
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::RequestInformation(
  vtkInformation*, vtkInformationVector**, vtkInformationVector*)
{
  // Do nothing and let subclasses handle it if needed
  return 1;
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::RequestUpdateTime(
  vtkInformation*, vtkInformationVector**, vtkInformationVector*)
{
  // Do nothing and let subclasses handle it if needed
  return 1;
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::RequestUpdateExtent(
  vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector*)
{
  int numInputPorts = this->GetNumberOfInputPorts();
  for (int i = 0; i < numInputPorts; ++i)
  {
    int numInputConnections = this->GetNumberOfInputConnections(i);
    for (int j = 0; j < numInputConnections; ++j)
    {
      vtkInformation* inputInfo = inputVector[i]->GetInformationObject(j);
      inputInfo->Set(vtkStreamingDemandDrivenPipeline::EXACT_EXTENT(), 1);
    }
  }
  return 1;
}

//------------------------------------------------------------------------------
int vtkHyperTreeGridAlgorithm::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  // Update progress
  this->UpdateProgress(0.);

  // Retrieve input and output
  vtkHyperTreeGrid* input = vtkHyperTreeGrid::GetData(inputVector[0], 0);
  if (!input)
  {
    vtkErrorMacro("No input available. Cannot proceed with hyper tree grid algorithm.");
    return 0;
  }
  vtkDataObject* outputDO = vtkDataObject::GetData(outputVector, 0);
  if (!outputDO)
  {
    vtkErrorMacro("No output available. Cannot proceed with hyper tree grid algorithm.");
    return 0;
  }

  this->OutData = nullptr;

  // Process all trees in input grid and generate input data object
  // only if extents are correct
  if ((input->GetExtent()[0] <= input->GetExtent()[1] ||
        input->GetExtent()[2] <= input->GetExtent()[3] ||
        input->GetExtent()[4] <= input->GetExtent()[5]) &&
    !this->ProcessTrees(input, outputDO))
  {
    return 0;
  }

  // Squeeze output data if present
  if (this->OutData)
  {
    this->OutData->Squeeze();
  }

  // Update progress and return
  this->UpdateProgress(1.);
  return 1;
}

//------------------------------------------------------------------------------
void vtkHyperTreeGridAlgorithm::SetInputData(vtkDataObject* input)
{
  this->SetInputData(0, input);
}

//------------------------------------------------------------------------------
void vtkHyperTreeGridAlgorithm::SetInputData(int index, vtkDataObject* input)
{
  this->SetInputDataInternal(index, input);
}

//------------------------------------------------------------------------------
void vtkHyperTreeGridAlgorithm::AddInputData(vtkDataObject* input)
{
  this->AddInputData(0, input);
}

//------------------------------------------------------------------------------
void vtkHyperTreeGridAlgorithm::AddInputData(int index, vtkDataObject* input)
{
  this->AddInputDataInternal(index, input);
}
VTK_ABI_NAMESPACE_END
