// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkPVMergeTables.h"

#include "vtkCompositeDataPipeline.h"
#include "vtkCompositeDataSetRange.h"
#include "vtkDataSetAttributes.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkObjectFactory.h"
#include "vtkSmartPointer.h"
#include "vtkTable.h"

vtkStandardNewMacro(vtkPVMergeTables);
//----------------------------------------------------------------------------
vtkPVMergeTables::vtkPVMergeTables() = default;

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

//----------------------------------------------------------------------------
int vtkPVMergeTables::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkTable");
  info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCompositeDataSet");
  return 1;
}

//----------------------------------------------------------------------------
vtkExecutive* vtkPVMergeTables::CreateDefaultExecutive()
{
  return vtkCompositeDataPipeline::New();
}

//----------------------------------------------------------------------------
void vtkPVMergeTables::MergeTables(vtkTable* output, const std::vector<vtkTable*>& tables)
{
  if (tables.empty())
  {
    return;
  }
  if (tables.size() == 1)
  {
    output->ShallowCopy(tables[0]);
    return;
  }

  vtkDataSetAttributes::FieldList fields;
  vtkIdType totalRows = 0;
  for (auto& curTable : tables)
  {
    if (curTable->GetNumberOfRows() > 0 && curTable->GetNumberOfColumns() > 0)
    {
      fields.IntersectFieldList(curTable->GetRowData());
      totalRows += curTable->GetNumberOfRows();
    }
  }

  auto outRD = output->GetRowData();
  // passing sz=0 ensures that fields simply uses the accumulated counts for
  // number of rows.
  fields.CopyAllocate(outRD, vtkDataSetAttributes::PASSDATA, totalRows, /*ext=*/0);

  vtkIdType outStartRow = 0;
  int fieldsInputIdx = 0;
  for (auto& curTable : tables)
  {
    if (curTable->GetNumberOfRows() == 0 || curTable->GetNumberOfColumns() == 0)
    {
      continue;
    }

    auto inRD = curTable->GetRowData();
    const auto inNumRows = inRD->GetNumberOfTuples();
    fields.CopyData(fieldsInputIdx, inRD, 0, inNumRows, outRD, outStartRow);
    outStartRow += inNumRows;
    ++fieldsInputIdx;
  }
  assert(outStartRow == totalRows);
}

//----------------------------------------------------------------------------
vtkSmartPointer<vtkTable> vtkPVMergeTables::MergeTables(const std::vector<vtkTable*>& tables)
{
  if (tables.empty())
  {
    return nullptr;
  }
  auto result = vtkSmartPointer<vtkTable>::New();
  vtkPVMergeTables::MergeTables(result, tables);
  return result;
}

//----------------------------------------------------------------------------
int vtkPVMergeTables::RequestData(
  vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  auto inputs = vtkPVMergeTables::GetTables(inputVector[0]);
  auto output = vtkTable::GetData(outputVector, 0);

  vtkPVMergeTables::MergeTables(output, inputs);
  return 1;
}

//----------------------------------------------------------------------------
std::vector<vtkTable*> vtkPVMergeTables::GetTables(vtkInformationVector* inputVector)
{
  std::vector<vtkTable*> tables;
  const int numInputs = inputVector->GetNumberOfInformationObjects();
  for (int inputIdx = 0; inputIdx < numInputs; ++inputIdx)
  {
    if (auto table = vtkTable::GetData(inputVector, inputIdx))
    {
      tables.push_back(table);
    }
    else if (auto cd = vtkCompositeDataSet::GetData(inputVector, inputIdx))
    {
      using Opts = vtk::CompositeDataSetOptions;
      for (auto node : vtk::Range(cd, Opts::SkipEmptyNodes))
      {
        if ((table = vtkTable::SafeDownCast(node.GetDataObject())))
        {
          tables.push_back(table);
        }
      }
    }
  }
  return tables;
}

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