/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkAppendSelection.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*----------------------------------------------------------------------------
 Copyright (c) Sandia Corporation
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
----------------------------------------------------------------------------*/
#include "vtkAppendSelection.h"

#include "vtkAlgorithmOutput.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkSelection.h"
#include "vtkSelectionNode.h"
#include "vtkSmartPointer.h"
#include "vtkStreamingDemandDrivenPipeline.h"

vtkStandardNewMacro(vtkAppendSelection);

//------------------------------------------------------------------------------
vtkAppendSelection::vtkAppendSelection()
{
  this->UserManagedInputs = 0;
  this->AppendByUnion = 1;
}

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

//------------------------------------------------------------------------------
// Add a dataset to the list of data to append.
void vtkAppendSelection::AddInputData(vtkSelection* ds)
{
  if (this->UserManagedInputs)
  {
    vtkErrorMacro(<< "AddInput is not supported if UserManagedInputs is true");
    return;
  }
  this->AddInputDataInternal(0, ds);
}

//------------------------------------------------------------------------------
// Remove a dataset from the list of data to append.
void vtkAppendSelection::RemoveInputData(vtkSelection* ds)
{
  if (this->UserManagedInputs)
  {
    vtkErrorMacro(<< "RemoveInput is not supported if UserManagedInputs is true");
    return;
  }

  if (!ds)
  {
    return;
  }
  int numCons = this->GetNumberOfInputConnections(0);
  for (int i = 0; i < numCons; i++)
  {
    if (this->GetInput(i) == ds)
    {
      this->RemoveInputConnection(0, this->GetInputConnection(0, i));
    }
  }
}

//------------------------------------------------------------------------------
// make ProcessObject function visible
// should only be used when UserManagedInputs is true.
void vtkAppendSelection::SetNumberOfInputs(int num)
{
  if (!this->UserManagedInputs)
  {
    vtkErrorMacro(<< "SetNumberOfInputs is not supported if UserManagedInputs is false");
    return;
  }

  // Ask the superclass to set the number of connections.
  this->SetNumberOfInputConnections(0, num);
}

//------------------------------------------------------------------------------
// Set Nth input, should only be used when UserManagedInputs is true.
void vtkAppendSelection::SetInputConnectionByNumber(int num, vtkAlgorithmOutput* input)
{
  if (!this->UserManagedInputs)
  {
    vtkErrorMacro(<< "SetInputByNumber is not supported if UserManagedInputs is false");
    return;
  }

  // Ask the superclass to connect the input.
  this->SetNthInputConnection(0, num, input);
}

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

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

  // If there are no inputs, we are done.
  int numInputs = this->GetNumberOfInputConnections(0);
  if (numInputs == 0)
  {
    return 1;
  }

  if (!this->AppendByUnion)
  {
    for (int idx = 0; idx < numInputs; ++idx)
    {
      vtkInformation* inInfo = inputVector[0]->GetInformationObject(idx);
      vtkSelection* sel = vtkSelection::GetData(inInfo);
      if (sel != nullptr)
      {
        for (unsigned int j = 0; j < sel->GetNumberOfNodes(); ++j)
        {
          vtkSelectionNode* inputNode = sel->GetNode(j);
          vtkSmartPointer<vtkSelectionNode> outputNode = vtkSmartPointer<vtkSelectionNode>::New();
          outputNode->ShallowCopy(inputNode);
          output->AddNode(outputNode);
        }
      }
    } // for each input
    return 1;
  }

  // The first non-null selection determines the required content type of all selections.
  int idx = 0;
  vtkSelection* first = nullptr;
  while (first == nullptr && idx < numInputs)
  {
    vtkInformation* inInfo = inputVector[0]->GetInformationObject(idx);
    first = vtkSelection::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
    idx++;
  }

  // If they are all null, return.
  if (first == nullptr)
  {
    return 1;
  }

  output->ShallowCopy(first);

  // Take the union of all non-null selections
  for (; idx < numInputs; ++idx)
  {
    vtkInformation* inInfo = inputVector[0]->GetInformationObject(idx);
    vtkSelection* s = vtkSelection::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
    if (s != nullptr)
    {
      output->Union(s);
    } // for a non nullptr input
  }   // for each input

  return 1;
}

//------------------------------------------------------------------------------
vtkSelection* vtkAppendSelection::GetInput(int idx)
{
  return vtkSelection::SafeDownCast(this->GetExecutive()->GetInputData(0, idx));
}

//------------------------------------------------------------------------------
int vtkAppendSelection::FillInputPortInformation(int port, vtkInformation* info)
{
  if (!this->Superclass::FillInputPortInformation(port, info))
  {
    return 0;
  }
  info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
  return 1;
}

//------------------------------------------------------------------------------
void vtkAppendSelection::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << "UserManagedInputs: " << (this->UserManagedInputs ? "On" : "Off") << endl;
  os << "AppendByUnion: " << (this->AppendByUnion ? "On" : "Off") << endl;
}
