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

// Input test to validate ability to read GE TRUCHAS files
#include "vtkCellData.h"
#include "vtkDoubleArray.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkNew.h"
#include "vtkPointData.h"
#include "vtkTRUCHASReader.h"
#include "vtkTestUtilities.h"
#include "vtkTesting.h"
#include "vtkUnstructuredGrid.h"

#include "vtkInformation.h"
#include "vtkStreamingDemandDrivenPipeline.h"

#include <iostream>

namespace
{
bool AE(double v1, double v2)
{
  if (fabs(v2 - v1) > 0.001)
  {
    std::cerr << v2 << "!=" << v1 << std::endl;
    return false;
  }
  return true;
}
}

int TestTRUCHASReader(int argc, char* argv[])
{
  vtkNew<vtkTesting> testing;
  testing->AddArguments(argc, argv);
  char* fileName =
    vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/TRUCHAS/viscoplastic-ring.h5");

  vtkNew<vtkTRUCHASReader> reader;
  reader->SetFileName(fileName);
  delete[] fileName;
  reader->UpdateInformation();
  int nb = reader->GetNumberOfBlockArrays();
  std::cerr << nb << " BLOCKS" << std::endl;
  for (int b = 0; b < nb; b++)
  {
    std::cerr << "BLOCK ID " << b << " named " << reader->GetBlockArrayName(b) << std::endl;
  }
  reader->SetBlockArrayStatus("1", 0); // block nums start at 1
  reader->SetBlockArrayStatus("2", 1); // block nums start at 1
  reader->SetBlockArrayStatus("3", 0); // block nums start at 1

  int nca = reader->GetNumberOfCellArrays();
  std::cerr << nca << " CELL ARRAYS" << std::endl;
  for (int a = 0; a < nca; a++)
  {
    std::cerr << "ARRAY " << a << " named " << reader->GetCellArrayName(a) << std::endl;
  }
  std::cerr << "IGNORE VOF" << std::endl;
  reader->SetCellArrayStatus("VOF", 0);

  int npa = reader->GetNumberOfPointArrays();
  std::cerr << npa << " POINT ARRAYS" << std::endl;
  for (int a = 0; a < npa; a++)
  {
    std::cerr << "ARRAY " << a << " named " << reader->GetPointArrayName(a) << std::endl;
  }
  std::cerr << "IGNORE Displacement" << std::endl;
  reader->SetPointArrayStatus("Displacement", 0);
  reader->Update();

  vtkUnstructuredGrid* grid = vtkUnstructuredGrid::SafeDownCast(reader->GetOutput()->GetBlock(1));
  if (!grid)
  {
    std::cerr << "Could not open first block of known good file" << std::endl;
    return EXIT_FAILURE;
  }
  int rnb = 0; // we produce empty blocks when deselected
  for (int b = 0; b < nb; b++)
  {
    if (reader->GetOutput()->GetBlock(b))
    {
      rnb++;
    }
  }
  if ((rnb != 1) || (reader->GetOutput()->GetNumberOfBlocks() != 3))
  {
    std::cerr << "Got unexpected number of blocks, found " << rnb << "/"
              << reader->GetOutput()->GetNumberOfBlocks() << " instead of " << 1 << "/" << 3
              << std::endl;
  }

  std::cerr << "---- CELL ARRAYS ----" << std::endl;
  const int expectedNumCArrays = nca - 1;
  if (nca > 0 && grid->GetCellData()->GetNumberOfArrays() != expectedNumCArrays)
  {
    std::cerr << "Got unexpected number of cell arrays, found "
              << grid->GetCellData()->GetNumberOfArrays() << " instead of " << expectedNumCArrays
              << std::endl;
    return EXIT_FAILURE;
  }
  for (int a = 0; a < grid->GetCellData()->GetNumberOfArrays(); a++)
  {
    vtkDataArray* da = grid->GetCellData()->GetArray(a);
    std::cerr << da->GetName() << std::endl;
  }

  std::cerr << "---- POINT ARRAYS ----" << std::endl;
  const int expectedNumPArrays = npa - 1;
  if (npa > 0 && grid->GetPointData()->GetNumberOfArrays() != expectedNumPArrays)
  {
    std::cerr << "Got unexpected number of point arrays, found "
              << grid->GetPointData()->GetNumberOfArrays() << " instead of " << expectedNumPArrays
              << std::endl;
    return EXIT_FAILURE;
  }
  for (int a = 0; a < grid->GetPointData()->GetNumberOfArrays(); a++)
  {
    vtkDataArray* da = grid->GetPointData()->GetArray(a);
    std::cerr << da->GetName() << std::endl;
  }

  const int expectedNumPoints = 496;
  if (grid->GetNumberOfPoints() != expectedNumPoints)
  {
    std::cerr << "Got unexpected number of points from file " << grid->GetNumberOfPoints()
              << " instead of " << expectedNumPoints << std::endl;
    return EXIT_FAILURE;
  }
  const int expectedNumCells = 180;
  if (grid->GetNumberOfCells() != expectedNumCells)
  {
    std::cerr << "Got unexpected number of cells from file " << grid->GetNumberOfCells()
              << " instead of " << expectedNumCells << std::endl;
    return EXIT_FAILURE;
  }

  vtkDoubleArray* da = vtkDoubleArray::SafeDownCast(grid->GetCellData()->GetArray("Grad_T"));
  if (!da)
  {
    std::cerr << "Couldn't get "
              << "Grad_T"
              << " array" << std::endl;
    return EXIT_FAILURE;
  }

  double* ptr = da->GetTuple(42);
  const double eVals[3] = { -10.4436, -4.32586, -10.4913 };

  if (!AE(*ptr, eVals[0]) || !AE(*(ptr + 1), eVals[1]) || !AE(*(ptr + 2), eVals[2]))
  {
    std::cerr << "Got unexpected values from Grad_T array for cell 42 " << *ptr << "," << *(ptr + 1)
              << "," << *(ptr + 2) << " instead of " << eVals[0] << "," << eVals[1] << ","
              << eVals[2] << std::endl;
    return EXIT_FAILURE;
  }
  reader->SetCellArrayStatus("VOF", 1);
  reader->Update();
  if (grid->GetCellData()->GetNumberOfArrays() != expectedNumCArrays + 1)
  {
    std::cerr << "Got unexpected number of cell arrays, found "
              << grid->GetCellData()->GetNumberOfArrays() << " instead of "
              << expectedNumCArrays + 1 << std::endl;
    return EXIT_FAILURE;
  }

  vtkInformation* inf = reader->GetExecutive()->GetOutputInformation(0);
  int numTimes = inf->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
  std::cerr << "FOUND " << numTimes << " timesteps " << std::endl;
  double tAlpha = inf->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), 0);
  double tOmega = inf->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), numTimes - 1);
  const int expectedNumTimes = 2;
  const double expectedMinT = 0.0;
  const double expectedMaxT = 0.5;
  if (numTimes != 2 || !AE(tAlpha, expectedMinT) || !AE(tOmega, expectedMaxT))
  {
    std::cerr << "Got unexpected times." << std::endl;
    std::cerr << numTimes << " not " << expectedNumTimes << " times ";
    std::cerr << tAlpha << " not " << expectedMinT << " first time";
    std::cerr << tOmega << " not " << expectedMaxT << " last time";
    return EXIT_FAILURE;
  }
  const int divs = 3;
  const double expectedRanges[divs][2] = {
    { 0, 0 },               // before
    { 0, 0 },               // after first
    { -1.99025, -0.85729 }, // after second
  };

  for (int i = 0; i < divs; i++)
  {
    double tNext = tAlpha - 0.1 + i * (tOmega - tAlpha) * 2.5 / divs;
    reader->UpdateTimeStep(tNext);
    grid = vtkUnstructuredGrid::SafeDownCast(reader->GetOutput()->GetBlock(1));
    da = vtkDoubleArray::SafeDownCast(grid->GetCellData()->GetArray("dT/dt"));
    double* mM = da->GetRange();
    std::cerr << "ts " << i << ":" << tNext << " got " << mM[0] << "," << mM[1] << std::endl;
    if (!AE(mM[0], expectedRanges[i][0]) || !AE(mM[1], expectedRanges[i][1]))
    {
      std::cerr << "Got unexpected ranges at time " << tNext << " " << mM[0] << "," << mM[1]
                << " instead of " << expectedRanges[i][0] << "," << expectedRanges[i][1]
                << std::endl;
      return EXIT_FAILURE;
    }
  }

  return EXIT_SUCCESS;
}
