// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright 2008 Sandia Corporation
// SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov

#include "vtkSQLGraphReader.h"

#include "vtkAssignCoordinates.h"
#include "vtkDirectedGraph.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkRowQueryToTable.h"
#include "vtkSQLQuery.h"
#include "vtkSmartPointer.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkTable.h"
#include "vtkTableToGraph.h"
#include "vtkUndirectedGraph.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkSQLGraphReader);

vtkSQLGraphReader::vtkSQLGraphReader()
{
  this->Directed = 0;
  this->CollapseEdges = 0;
  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);
  this->VertexQuery = nullptr;
  this->EdgeQuery = nullptr;
  this->SourceField = 0;
  this->TargetField = 0;
  this->VertexIdField = 0;
  this->XField = 0;
  this->YField = 0;
  this->ZField = 0;
}

vtkSQLGraphReader::~vtkSQLGraphReader()
{
  if (this->VertexQuery != nullptr)
  {
    this->VertexQuery->Delete();
  }
  if (this->EdgeQuery != nullptr)
  {
    this->EdgeQuery->Delete();
  }
  this->SetSourceField(0);
  this->SetTargetField(0);
  this->SetVertexIdField(0);
  this->SetXField(0);
  this->SetYField(0);
  this->SetZField(0);
}

void vtkSQLGraphReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "Directed: " << this->Directed << endl;
  os << indent << "CollapseEdges: " << this->CollapseEdges << endl;
  os << indent << "XField: " << (this->XField ? this->XField : "(null)") << endl;
  os << indent << "YField: " << (this->YField ? this->YField : "(null)") << endl;
  os << indent << "ZField: " << (this->ZField ? this->ZField : "(null)") << endl;
  os << indent << "VertexIdField: " << (this->VertexIdField ? this->VertexIdField : "(null)")
     << endl;
  os << indent << "SourceField: " << (this->SourceField ? this->SourceField : "(null)") << endl;
  os << indent << "TargetField: " << (this->TargetField ? this->TargetField : "(null)") << endl;
  os << indent << "EdgeQuery: " << (this->EdgeQuery ? "" : "(null)") << endl;
  if (this->EdgeQuery)
  {
    this->EdgeQuery->PrintSelf(os, indent.GetNextIndent());
  }
  os << indent << "VertexQuery: " << (this->VertexQuery ? "" : "(null)") << endl;
  if (this->VertexQuery)
  {
    this->VertexQuery->PrintSelf(os, indent.GetNextIndent());
  }
}

vtkCxxSetObjectMacro(vtkSQLGraphReader, VertexQuery, vtkSQLQuery);
vtkCxxSetObjectMacro(vtkSQLGraphReader, EdgeQuery, vtkSQLQuery);

int vtkSQLGraphReader::RequestData(
  vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector)
{
  // Check for valid inputs
  if (this->EdgeQuery == nullptr)
  {
    vtkErrorMacro("The EdgeQuery must be defined");
    return 0;
  }
  if (this->SourceField == nullptr)
  {
    vtkErrorMacro("The SourceField must be defined");
    return 0;
  }
  if (this->TargetField == nullptr)
  {
    vtkErrorMacro("The TargetField must be defined");
    return 0;
  }
  if (this->VertexQuery != nullptr)
  {
    if (this->VertexIdField == nullptr)
    {
      vtkErrorMacro("The VertexIdField must be defined when using a VertexQuery");
      return 0;
    }
    if (this->XField != nullptr && this->YField == nullptr)
    {
      vtkErrorMacro("The YField must be defined if the XField is defined");
      return 0;
    }
  }

  vtkGraph* output = vtkGraph::GetData(outputVector);

  vtkTableToGraph* filter = vtkTableToGraph::New();
  filter->SetDirected(this->Directed);

  // Set up the internal filter to use the edge table
  vtkSmartPointer<vtkRowQueryToTable> edgeReader = vtkSmartPointer<vtkRowQueryToTable>::New();
  edgeReader->SetQuery(this->EdgeQuery);
  edgeReader->Update();

  const char* domain = "default";
  if (this->VertexIdField)
  {
    domain = this->VertexIdField;
  }

  filter->SetInputConnection(edgeReader->GetOutputPort());
  filter->AddLinkVertex(this->SourceField, domain);
  filter->AddLinkVertex(this->TargetField, domain);
  filter->AddLinkEdge(this->SourceField, this->TargetField);

  vtkSmartPointer<vtkAssignCoordinates> assign = vtkSmartPointer<vtkAssignCoordinates>::New();
  assign->SetInputConnection(filter->GetOutputPort());

  // Set up the internal filter to use the vertex table
  if (this->VertexQuery != nullptr)
  {
    vtkSmartPointer<vtkRowQueryToTable> vertexReader = vtkSmartPointer<vtkRowQueryToTable>::New();
    vertexReader->SetQuery(this->VertexQuery);
    vertexReader->Update();
    filter->SetInputConnection(1, vertexReader->GetOutputPort());
    if (this->XField != nullptr)
    {
      assign->SetXCoordArrayName(this->XField);
      assign->SetYCoordArrayName(this->YField);
      if (this->ZField != nullptr)
      {
        assign->SetZCoordArrayName(this->ZField);
      }
    }
  }

  // Get the internal filter output and assign it to the output
  if (this->XField != nullptr)
  {
    assign->Update();
    vtkGraph* assignOutput = vtkGraph::SafeDownCast(assign->GetOutput());
    output->ShallowCopy(assignOutput);
  }
  else
  {
    filter->Update();
    vtkGraph* filterOutput = filter->GetOutput();
    output->ShallowCopy(filterOutput);
  }

  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  int piece = -1;
  int npieces = -1;
  if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()))
  {
    piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
    npieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
  }
  output->GetInformation()->Set(vtkDataObject::DATA_NUMBER_OF_PIECES(), npieces);
  output->GetInformation()->Set(vtkDataObject::DATA_PIECE_NUMBER(), piece);

  // Clean up
  filter->Delete();

  return 1;
}

int vtkSQLGraphReader::RequestDataObject(
  vtkInformation*, vtkInformationVector**, vtkInformationVector*)
{
  vtkDataObject* current = this->GetExecutive()->GetOutputData(0);
  if (!current || (this->Directed && !vtkDirectedGraph::SafeDownCast(current)) ||
    (!this->Directed && vtkDirectedGraph::SafeDownCast(current)))
  {
    vtkGraph* output = 0;
    if (this->Directed)
    {
      output = vtkDirectedGraph::New();
    }
    else
    {
      output = vtkUndirectedGraph::New();
    }
    this->GetExecutive()->SetOutputData(0, output);
    output->Delete();
  }

  return 1;
}
VTK_ABI_NAMESPACE_END
