// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright 2007 Sandia Corporation
// SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov
#include "vtkGenerateIndexArray.h"
#include "vtkCellData.h"
#include "vtkDataSet.h"
#include "vtkDataSetAttributes.h"
#include "vtkDemandDrivenPipeline.h"
#include "vtkGraph.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkTable.h"

#include <map>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkGenerateIndexArray);

vtkGenerateIndexArray::vtkGenerateIndexArray()
  : ArrayName(nullptr)
  , FieldType(ROW_DATA)
  , ReferenceArrayName(nullptr)
  , PedigreeID(false)
{
  this->SetArrayName("index");
}

vtkGenerateIndexArray::~vtkGenerateIndexArray()
{
  this->SetArrayName(nullptr);
  this->SetReferenceArrayName(nullptr);
}

void vtkGenerateIndexArray::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << "ArrayName: " << (this->ArrayName ? this->ArrayName : "(none)") << endl;
  os << "FieldType: " << this->FieldType << endl;
  os << "ReferenceArrayName: " << (this->ReferenceArrayName ? this->ReferenceArrayName : "(none)")
     << endl;
  os << "PedigreeID: " << this->PedigreeID << endl;
}

vtkTypeBool vtkGenerateIndexArray::ProcessRequest(
  vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA_OBJECT()))
  {
    return this->RequestDataObject(request, inputVector, outputVector);
  }
  return this->Superclass::ProcessRequest(request, inputVector, outputVector);
}

int vtkGenerateIndexArray::RequestDataObject(
  vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  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;
  }
  return 0;
}

int vtkGenerateIndexArray::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  // An output array name is required ...
  if (!(this->ArrayName && strlen(this->ArrayName)))
  {
    vtkErrorMacro(<< "No array name defined.");
    return 0;
  }

  // Make a shallow-copy of our input ...
  vtkDataObject* const input = vtkDataObject::GetData(inputVector[0]);
  vtkDataObject* const output = vtkDataObject::GetData(outputVector);
  output->ShallowCopy(input);

  // Figure-out where we'll be reading/writing data ...
  vtkDataSetAttributes* output_attributes = nullptr;
  vtkIdType output_count = 0;

  switch (this->FieldType)
  {
    case ROW_DATA:
    {
      vtkTable* const table = vtkTable::SafeDownCast(output);
      output_attributes = table ? table->GetRowData() : nullptr;
      output_count = table ? table->GetNumberOfRows() : 0;
      break;
    }
    case POINT_DATA:
    {
      vtkDataSet* const data_set = vtkDataSet::SafeDownCast(output);
      output_attributes = data_set ? data_set->GetPointData() : nullptr;
      output_count = data_set ? data_set->GetNumberOfPoints() : 0;
      break;
    }
    case CELL_DATA:
    {
      vtkDataSet* const data_set = vtkDataSet::SafeDownCast(output);
      output_attributes = data_set ? data_set->GetCellData() : nullptr;
      output_count = data_set ? data_set->GetNumberOfCells() : 0;
      break;
    }
    case VERTEX_DATA:
    {
      vtkGraph* const graph = vtkGraph::SafeDownCast(output);
      output_attributes = graph ? graph->GetVertexData() : nullptr;
      output_count = graph ? graph->GetNumberOfVertices() : 0;
      break;
    }
    case EDGE_DATA:
    {
      vtkGraph* const graph = vtkGraph::SafeDownCast(output);
      output_attributes = graph ? graph->GetEdgeData() : nullptr;
      output_count = graph ? graph->GetNumberOfEdges() : 0;
      break;
    }
  }

  if (!output_attributes)
  {
    vtkErrorMacro(<< "Invalid field type for this data object.");
    return 0;
  }

  // Create our output array ...
  vtkIdTypeArray* const output_array = vtkIdTypeArray::New();
  output_array->SetName(this->ArrayName);
  output_array->SetNumberOfTuples(output_count);
  output_attributes->AddArray(output_array);
  output_array->Delete();

  if (this->PedigreeID)
    output_attributes->SetPedigreeIds(output_array);

  // Generate indices based on the reference array ...
  if (this->ReferenceArrayName && strlen(this->ReferenceArrayName))
  {
    int reference_array_index = -1;
    vtkAbstractArray* const reference_array =
      output_attributes->GetAbstractArray(this->ReferenceArrayName, reference_array_index);
    if (!reference_array)
    {
      vtkErrorMacro(<< "No reference array " << this->ReferenceArrayName);
      return 0;
    }

    typedef std::map<vtkVariant, vtkIdType, vtkVariantLessThan> index_map_t;
    index_map_t index_map;

    for (vtkIdType i = 0; i != output_count; ++i)
    {
      if (!index_map.count(reference_array->GetVariantValue(i)))
      {
#ifdef _RWSTD_NO_MEMBER_TEMPLATES
        // Deal with Sun Studio old libCstd.
        // http://sahajtechstyle.blogspot.com/2007/11/whats-wrong-with-sun-studio-c.html
        index_map.insert(
          std::pair<const vtkVariant, vtkIdType>(reference_array->GetVariantValue(i), 0));
#else
        index_map.insert(std::make_pair(reference_array->GetVariantValue(i), 0));
#endif
      }
    }

    vtkIdType index = 0;
    for (index_map_t::iterator i = index_map.begin(); i != index_map.end(); ++i, ++index)
      i->second = index;

    for (vtkIdType i = 0; i != output_count; ++i)
      output_array->SetValue(i, index_map[reference_array->GetVariantValue(i)]);
  }
  // Otherwise, generate a trivial index array ...
  else
  {
    for (vtkIdType i = 0; i != output_count; ++i)
      output_array->SetValue(i, i);
  }

  return 1;
}
VTK_ABI_NAMESPACE_END
