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

#include "vtkmNDHistogram.h"

#include "vtkActor.h"
#include "vtkArrayData.h"
#include "vtkCamera.h"
#include "vtkCellData.h"
#include "vtkDataSet.h"
#include "vtkDelaunay3D.h"
#include "vtkDoubleArray.h"
#include "vtkImageData.h"
#include "vtkImageToPoints.h"
#include "vtkNew.h"
#include "vtkPointData.h"
#include "vtkSparseArray.h"
#include "vtkTable.h"

#include <iostream>

namespace
{
const std::vector<std::string> arrayNames = { "temperature0", "temperature1", "temperature2",
  "temperature3" };
const std::vector<std::vector<size_t>> resultBins = {
  { 0, 0, 1, 1, 2, 2, 3, 3 },
  { 0, 1, 1, 2, 2, 3, 3, 4 },
  { 0, 1, 2, 2, 3, 4, 4, 5 },
  { 0, 1, 2, 3, 3, 4, 5, 6 },
};
const std::vector<size_t> resultFrequency = { 2, 1, 1, 1, 1, 1, 1, 2 };
const int nData = 10;
const std::vector<size_t> bins = { 4, 5, 6, 7 };
void AddArrayToVTKData(
  std::string scalarName, vtkDataSetAttributes* pd, double* data, vtkIdType size)
{
  vtkNew<vtkDoubleArray> scalars;
  scalars->SetArray(data, size, 1);
  scalars->SetName(scalarName.c_str());
  pd->AddArray(scalars);
}

void MakeTestDataset(vtkDataSet* dataset)
{
  static double T0[nData], T1[nData], T2[nData], T3[nData];
  for (int i = 0; i < nData; i++)
  {
    T0[i] = i * 1.0;
    T1[i] = i * 2.0;
    T2[i] = i * 3.0;
    T3[i] = i * 4.0;
  }

  vtkPointData* pd = dataset->GetPointData();
  AddArrayToVTKData(arrayNames[0], pd, T0, static_cast<vtkIdType>(nData));
  AddArrayToVTKData(arrayNames[1], pd, T1, static_cast<vtkIdType>(nData));
  AddArrayToVTKData(arrayNames[2], pd, T2, static_cast<vtkIdType>(nData));
  AddArrayToVTKData(arrayNames[3], pd, T3, static_cast<vtkIdType>(nData));
}
}

int TestVTKMNDHistogram(int, char*[])
{
  vtkNew<vtkPolyData> ds;
  MakeTestDataset(ds);

  vtkNew<vtkmNDHistogram> filter;
  filter->SetInputData(ds);
  size_t index = 0;
  for (const auto& an : arrayNames)
  {
    filter->AddFieldAndBin(an, bins[index++]);
  }
  filter->Update();
  vtkArrayData* arrayData = filter->GetOutput();

  assert(arrayData != nullptr);
  // Valid the data range and bin delta
  for (vtkIdType i = 0; i < 4; i++)
  {
    // Validate the delta and range
    auto range = filter->GetDataRange(i);
    double delta = filter->GetBinDelta(i);
    if (range.first != 0.0 || range.second != (i + 1) * 9)
    {
      std::cout << "array index=" << i << " does not have right range" << std::endl;
      return 1;
    }
    if (delta != ((range.second - range.first) / bins[i]))
    {
      std::cout << "array index" << i << " does not have right delta" << std::endl;
      return 1;
    }
  }
  vtkSparseArray<double>* sa = static_cast<vtkSparseArray<double>*>(arrayData->GetArray(0));
  vtkArrayCoordinates coordinates;
  const vtkIdType dimensions = sa->GetDimensions();     // 4
  const vtkIdType non_null_size = sa->GetNonNullSize(); // 8
  for (vtkIdType n = 0; n != non_null_size; ++n)
  {
    sa->GetCoordinatesN(n, coordinates);
    for (vtkIdType d = 0; d != dimensions; ++d)
    {
      assert(coordinates[d] == static_cast<vtkIdType>(resultBins[d][n]));
      if (coordinates[d] != static_cast<vtkIdType>(resultBins[d][n]))
      {
        std::cout << "value does not match at index " << n << " dimension " << d << std::endl;
      }
    }
    assert(resultFrequency[n] == sa->GetValue(coordinates));
  }

  (void)resultFrequency; // Avoid unused-variable warning when NDEBUG is defined
  return 0;
}
