// 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 <vtkDelimitedTextReader.h>
#include <vtkIOStream.h>
#include <vtkMergeGraphs.h>
#include <vtkSmartPointer.h>
#include <vtkStringArray.h>
#include <vtkTable.h>
#include <vtkTableToGraph.h>
#include <vtkTestUtilities.h>
#include <vtkVariant.h>
#include <vtkVariantArray.h>

#include <string>

#include <iostream>

template <typename T, size_t Cols>
void BuildTable(vtkTable* table, T values[][Cols], int rows)
{
  for (size_t c = 0; c < Cols; ++c)
  {
    vtkSmartPointer<vtkStringArray> arr = vtkSmartPointer<vtkStringArray>::New();
    arr->SetName(vtkVariant(values[0][c]).ToString().c_str());
    for (int r = 0; r < rows; ++r)
    {
      arr->InsertNextValue(values[r + 1][c]);
    }
    table->AddColumn(arr);
  }
}

bool CheckTable(vtkTable* expected, vtkTable* output)
{
  bool ok = true;
  for (vtkIdType col = 0; col < expected->GetNumberOfColumns(); ++col)
  {
    vtkStringArray* exp_arr = vtkArrayDownCast<vtkStringArray>(expected->GetColumn(col));
    vtkStringArray* out_arr =
      vtkArrayDownCast<vtkStringArray>(output->GetColumnByName(exp_arr->GetName()));
    if (!out_arr)
    {
      std::cerr << "Output array " << exp_arr->GetName() << " does not exist" << std::endl;
      ok = false;
      continue;
    }
    if (out_arr->GetNumberOfTuples() != exp_arr->GetNumberOfTuples())
    {
      std::cerr << "Output array " << exp_arr->GetName() << " has " << out_arr->GetNumberOfTuples()
                << " tuples when " << exp_arr->GetNumberOfTuples() << " were expected."
                << std::endl;
      ok = false;
      continue;
    }
    for (vtkIdType row = 0; row < exp_arr->GetNumberOfTuples(); ++row)
    {
      if (exp_arr->GetValue(row) != out_arr->GetValue(row))
      {
        std::cerr << "Output array " << exp_arr->GetName() << " has " << out_arr->GetValue(row)
                  << " at position " << row << " when " << exp_arr->GetValue(row)
                  << " was expected." << std::endl;
        ok = false;
      }
    }
  }
  return ok;
}

const std::string vert_data1[][3] = { { "id", "arr1", "arr2" }, { "v1", "a", "d" },
  { "v2", "b", "e" }, { "v3", "c", "f" } };

const std::string vert_data2[][3] = { { "id", "arr2", "arr3" }, { "v2", "g", "j" },
  { "v3", "h", "k" }, { "v4", "i", "l" } };

const std::string edge_data1[][4] = { { "id", "src", "tgt", "extra" }, { "e1", "v1", "v2", "m" },
  { "e2", "v2", "v3", "n" }, { "e3", "v3", "v1", "o" } };

const std::string edge_data2[][3] = { { "id", "src", "tgt" }, { "e4", "v2", "v3" },
  { "e5", "v3", "v4" }, { "e6", "v4", "v2" } };

const std::string expected_vert_data[][3] = { { "id", "arr1", "arr2" }, { "v1", "a", "d" },
  { "v2", "b", "e" }, { "v3", "c", "f" }, { "v4", "", "i" } };

const std::string expected_edge_data[][4] = { { "id", "src", "tgt", "extra" },
  { "e1", "v1", "v2", "m" }, { "e2", "v2", "v3", "n" }, { "e3", "v3", "v1", "o" },
  { "e4", "v2", "v3", "" }, { "e6", "v4", "v2", "" }, { "e5", "v3", "v4", "" } };

int TestMergeGraphs(int, char*[])
{
  vtkSmartPointer<vtkTable> vert_table1 = vtkSmartPointer<vtkTable>::New();
  BuildTable(vert_table1, vert_data1, 3);
  vtkSmartPointer<vtkTable> vert_table2 = vtkSmartPointer<vtkTable>::New();
  BuildTable(vert_table2, vert_data2, 3);
  vtkSmartPointer<vtkTable> edge_table1 = vtkSmartPointer<vtkTable>::New();
  BuildTable(edge_table1, edge_data1, 3);
  vtkSmartPointer<vtkTable> edge_table2 = vtkSmartPointer<vtkTable>::New();
  BuildTable(edge_table2, edge_data2, 3);

  vtkSmartPointer<vtkTableToGraph> ttg1 = vtkSmartPointer<vtkTableToGraph>::New();
  ttg1->SetInputData(0, edge_table1);
  ttg1->SetInputData(1, vert_table1);
  ttg1->AddLinkVertex("src", "id");
  ttg1->AddLinkVertex("tgt", "id");
  ttg1->AddLinkEdge("src", "tgt");
  vtkSmartPointer<vtkTableToGraph> ttg2 = vtkSmartPointer<vtkTableToGraph>::New();
  ttg2->SetInputData(0, edge_table2);
  ttg2->SetInputData(1, vert_table2);
  ttg2->AddLinkVertex("src", "id");
  ttg2->AddLinkVertex("tgt", "id");
  ttg2->AddLinkEdge("src", "tgt");

  vtkSmartPointer<vtkMergeGraphs> merge = vtkSmartPointer<vtkMergeGraphs>::New();
  merge->SetInputConnection(0, ttg1->GetOutputPort());
  merge->SetInputConnection(1, ttg2->GetOutputPort());

  merge->Update();

  vtkSmartPointer<vtkTable> output_vert_table = vtkSmartPointer<vtkTable>::New();
  output_vert_table->SetRowData(merge->GetOutput()->GetVertexData());
  output_vert_table->Dump(10);

  vtkSmartPointer<vtkTable> output_edge_table = vtkSmartPointer<vtkTable>::New();
  output_edge_table->SetRowData(merge->GetOutput()->GetEdgeData());
  output_edge_table->Dump(10);

  // Check the results
  int ret = 0;
  vtkSmartPointer<vtkTable> expected_vert_table = vtkSmartPointer<vtkTable>::New();
  BuildTable(expected_vert_table, expected_vert_data, 4);
  if (!CheckTable(expected_vert_table, output_vert_table))
  {
    ret = 1;
  }
  vtkSmartPointer<vtkTable> expected_edge_table = vtkSmartPointer<vtkTable>::New();
  BuildTable(expected_edge_table, expected_edge_data, 6);
  if (!CheckTable(expected_edge_table, output_edge_table))
  {
    ret = 1;
  }

  return ret;
}
