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

#include <vtkCellData.h>
#include <vtkDataSetAttributes.h>
#include <vtkDoubleArray.h>
#include <vtkIdList.h>
#include <vtkIntArray.h>
#include <vtkMath.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkSmartPointer.h>
#include <vtkStructuredGrid.h>
#include <vtkStructuredGridAppend.h>

namespace
{
const char arrayName[] = "coordinates";

//////////////////////////////////////////////////////////////////////////////
// Create a dataset for testing
//////////////////////////////////////////////////////////////////////////////
void CreateDataset(vtkStructuredGrid* dataset, int extent[6])
{
  dataset->SetExtent(extent);
  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
  // create a point data array
  vtkNew<vtkDoubleArray> pointArray;
  pointArray->SetName(arrayName);
  dataset->GetPointData()->AddArray(pointArray);
  for (int k = extent[4]; k <= extent[5]; k++)
  {
    for (int j = extent[2]; j <= extent[3]; j++)
    {
      for (int i = extent[0]; i <= extent[1]; i++)
      {
        points->InsertNextPoint((double)i, (double)j, (double)k);
        pointArray->InsertNextValue((double)i);
      }
    }
  }
  dataset->SetPoints(points);

  // create a cell data array
  vtkNew<vtkIntArray> cellArray;
  cellArray->SetName(arrayName);
  cellArray->SetNumberOfComponents(3);
  dataset->GetCellData()->AddArray(cellArray);
  int ijk[3];
  for (int k = extent[4]; k < extent[5]; k++)
  {
    ijk[2] = k;
    for (int j = extent[2]; j < extent[3]; j++)
    {
      ijk[1] = j;
      for (int i = extent[0]; i < extent[1]; i++)
      {
        ijk[0] = i;
        cellArray->InsertNextTypedTuple(ijk);
      }
    }
  }
}

//////////////////////////////////////////////////////////////////////////////
// Returns 1 on success, 0 otherwise
//////////////////////////////////////////////////////////////////////////////
int AppendDatasetsAndCheck(
  const std::vector<vtkSmartPointer<vtkStructuredGrid>>& inputs, int outputExtent[6])
{
  vtkNew<vtkStructuredGridAppend> append;
  for (size_t inputIndex = 0; inputIndex < inputs.size(); ++inputIndex)
  {
    append->AddInputData(inputs[inputIndex]);
  }
  append->Update();

  vtkStructuredGrid* output = append->GetOutput();
  int extent[6];
  output->GetExtent(extent);
  for (int i = 0; i < 6; i++)
  {
    if (extent[i] != outputExtent[i])
    {
      vtkGenericWarningMacro("ERROR: Extent is wrong.");
      return 0;
    }
  }

  if (vtkDoubleArray* pointArray =
        vtkArrayDownCast<vtkDoubleArray>(output->GetPointData()->GetArray(arrayName)))
  {
    vtkIdType counter = 0;
    for (int k = extent[4]; k <= extent[5]; k++)
    {
      for (int j = extent[2]; j <= extent[3]; j++)
      {
        for (int i = extent[0]; i <= extent[1]; i++)
        {
          double value = pointArray->GetValue(counter);
          if (value != (double)i)
          {
            vtkGenericWarningMacro(
              "ERROR: Bad point array value " << value << " which should be " << (double)i);
            return 0;
          }
          counter++;
        }
      }
    }
  }
  else
  {
    vtkGenericWarningMacro("ERROR: Could not find point data array.");
    return 0;
  }

  if (vtkIntArray* cellArray =
        vtkArrayDownCast<vtkIntArray>(output->GetCellData()->GetArray(arrayName)))
  {
    vtkIdType counter = 0;
    for (int k = extent[4]; k < extent[5]; k++)
    {
      for (int j = extent[2]; j < extent[3]; j++)
      {
        for (int i = extent[0]; i < extent[1]; i++)
        {
          int values[3];
          cellArray->GetTypedTuple(counter, values);
          if (values[0] != i || values[1] != j || values[2] != k)
          {
            vtkGenericWarningMacro("ERROR: Bad cell array tuple value ["
              << values[0] << ", " << values[1] << ", " << values[2] << "] which should be [" << i
              << ", " << j << ", " << k << "]");
            return 0;
          }
          counter++;
        }
      }
    }
  }
  else
  {
    vtkGenericWarningMacro("ERROR: Could not find cell data array.");
    return 0;
  }

  return 1;
}

} // end anonymous namespace

//////////////////////////////////////////////////////////////////////////////
int TestStructuredGridAppend(int, char*[])
{
  std::vector<vtkSmartPointer<vtkStructuredGrid>> inputs;
  int outputExtent[6] = { -1, 19, 0, 4, 0, 5 };
  for (int i = 0; i < 3; i++)
  {
    int extent[6] = { i * 6 - 1, (i + 1) * 6 + 1, 0, 4, 0, 5 };
    vtkSmartPointer<vtkStructuredGrid> dataset = vtkSmartPointer<vtkStructuredGrid>::New();
    CreateDataset(dataset, extent);
    inputs.push_back(dataset);
  }

  if (!AppendDatasetsAndCheck(inputs, outputExtent))
  {
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
