// 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 <vtkArrayData.h>
#include <vtkArrayPrint.h>
#include <vtkDenseArray.h>
#include <vtkDotProductSimilarity.h>
#include <vtkSmartPointer.h>
#include <vtkTable.h>
#include <vtkVariant.h>

#include <iostream>
#include <stdexcept>

#define test_expression(expression)                                                                \
  do                                                                                               \
  {                                                                                                \
    if (!(expression))                                                                             \
      throw std::runtime_error("Expression failed: " #expression);                                 \
  } while (false)

#if 0
static bool close_enough(const double lhs, const double rhs)
{
  return fabs(lhs - rhs) < 1.0e-12;
}
#endif

int ArrayDotProductSimilarity(int vtkNotUsed(argc), char* vtkNotUsed(argv)[])
{
  std::cout << setprecision(17);

  try
  {
    // Run tests on one matrix ...
    vtkSmartPointer<vtkDenseArray<double>> matrix_a = vtkSmartPointer<vtkDenseArray<double>>::New();
    matrix_a->Resize(vtkArrayExtents(2, 2));
    matrix_a->SetValue(vtkArrayCoordinates(0, 0), 1);
    matrix_a->SetValue(vtkArrayCoordinates(1, 0), 2);
    matrix_a->SetValue(vtkArrayCoordinates(0, 1), 3);
    matrix_a->SetValue(vtkArrayCoordinates(1, 1), 4);

    vtkSmartPointer<vtkArrayData> matrix_data_a = vtkSmartPointer<vtkArrayData>::New();
    matrix_data_a->AddArray(matrix_a);

    vtkSmartPointer<vtkDotProductSimilarity> similarity =
      vtkSmartPointer<vtkDotProductSimilarity>::New();
    similarity->SetInputData(0, matrix_data_a);
    similarity->SetVectorDimension(1);
    similarity->SetMinimumThreshold(0);
    similarity->SetMinimumCount(0);

    similarity->SetUpperDiagonal(true);
    similarity->SetDiagonal(false);
    similarity->SetLowerDiagonal(false);
    similarity->Update();
    similarity->GetOutput()->Dump(10);

    test_expression(similarity->GetOutput()->GetNumberOfRows() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(0, "source").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "target").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(0, "similarity").ToInt() == 11);

    similarity->SetUpperDiagonal(false);
    similarity->SetDiagonal(true);
    similarity->SetLowerDiagonal(false);
    similarity->Update();
    similarity->GetOutput()->Dump(10);
    test_expression(similarity->GetOutput()->GetValueByName(0, "source").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "target").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "similarity").ToInt() == 5);
    test_expression(similarity->GetOutput()->GetValueByName(1, "source").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(1, "target").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(1, "similarity").ToInt() == 25);

    test_expression(similarity->GetOutput()->GetNumberOfRows() == 2);

    similarity->SetUpperDiagonal(false);
    similarity->SetDiagonal(false);
    similarity->SetLowerDiagonal(true);
    similarity->Update();
    similarity->GetOutput()->Dump(10);

    test_expression(similarity->GetOutput()->GetNumberOfRows() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(0, "source").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(0, "target").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "similarity").ToInt() == 11);

    // Run tests with two matrices ...
    vtkSmartPointer<vtkDenseArray<double>> matrix_b = vtkSmartPointer<vtkDenseArray<double>>::New();
    matrix_b->Resize(vtkArrayExtents(2, 2));
    matrix_b->SetValue(vtkArrayCoordinates(0, 0), 5);
    matrix_b->SetValue(vtkArrayCoordinates(1, 0), 6);
    matrix_b->SetValue(vtkArrayCoordinates(0, 1), 7);
    matrix_b->SetValue(vtkArrayCoordinates(1, 1), 8);

    vtkSmartPointer<vtkArrayData> matrix_data_b = vtkSmartPointer<vtkArrayData>::New();
    matrix_data_b->AddArray(matrix_b);

    similarity->SetInputData(1, matrix_data_b);

    similarity->SetFirstSecond(true);
    similarity->SetSecondFirst(false);
    similarity->Update();
    similarity->GetOutput()->Dump(10);

    test_expression(similarity->GetOutput()->GetNumberOfRows() == 4);
    test_expression(similarity->GetOutput()->GetValueByName(0, "source").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "target").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "similarity").ToInt() == 17);
    test_expression(similarity->GetOutput()->GetValueByName(1, "source").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(1, "target").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(1, "similarity").ToInt() == 23);
    test_expression(similarity->GetOutput()->GetValueByName(2, "source").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(2, "target").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(2, "similarity").ToInt() == 39);
    test_expression(similarity->GetOutput()->GetValueByName(3, "source").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(3, "target").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(3, "similarity").ToInt() == 53);

    similarity->SetFirstSecond(false);
    similarity->SetSecondFirst(true);
    similarity->Update();
    similarity->GetOutput()->Dump(10);

    test_expression(similarity->GetOutput()->GetNumberOfRows() == 4);
    test_expression(similarity->GetOutput()->GetValueByName(0, "source").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "target").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(0, "similarity").ToInt() == 17);
    test_expression(similarity->GetOutput()->GetValueByName(1, "source").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(1, "target").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(1, "similarity").ToInt() == 39);
    test_expression(similarity->GetOutput()->GetValueByName(2, "source").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(2, "target").ToInt() == 0);
    test_expression(similarity->GetOutput()->GetValueByName(2, "similarity").ToInt() == 23);
    test_expression(similarity->GetOutput()->GetValueByName(3, "source").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(3, "target").ToInt() == 1);
    test_expression(similarity->GetOutput()->GetValueByName(3, "similarity").ToInt() == 53);

    return 0;
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
    return 1;
  }
}
