//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//
//============================================================================

#include <fides/DataSetReader.h>
#ifdef FIDES_USE_MPI
#include <mpi.h>
#endif
#include <numeric>
#include <string>
#include <unordered_map>

#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandleBasic.h>
#include <vtkm/cont/ArrayRangeCompute.h>
#include <vtkm/io/VTKDataSetWriter.h>

template <typename T>
static bool CheckField(const vtkm::cont::DataSet& ds,
                       const std::string& vname,
                       const vtkm::Range& range,
                       const vtkm::Float64& tolerance)
{
  if (!ds.HasField(vname))
  {
    std::cerr << "Error: Field not found: " << vname << std::endl;
    return false;
  }
  vtkm::Range rangeWithTol(range.Min - tolerance, range.Max + tolerance);

  vtkm::cont::ArrayHandle<vtkm::Range> rangeArray = ds.GetField(vname).GetRange();
  auto vRange = rangeArray.ReadPortal().Get(0);

  if (!rangeWithTol.Contains(vRange.Min) || !rangeWithTol.Contains(vRange.Max))
  {
    std::cerr << "Range for is incorrect for : " << vname << std::endl;
    return false;
  }

  return true;
}

int main(int argc, char** argv)
{
  if (argc < 3)
  {
    std::cerr << "Usage ./test-gx <name of the json file> <path of data source folder>"
                 "<optional write data>\n";
    std::cerr << "Example: ./test-gx ~/fides/tests/test-gx.json ~/fides/tests/data 1\n"
                 "\tThis outputs the data in .vtk format.\n";
    return 0;
  }

#ifdef FIDES_USE_MPI
  MPI_Init(&argc, &argv);
#endif
  bool writeVTK = false;
  if (argc == 4)
  {
    writeVTK = std::atoi(argv[3]) == 1;
  }

  std::cerr << " Reading: " << argv[1] << std::endl;
  int retVal = 0;
  fides::io::DataSetReader reader(argv[1]);
  std::unordered_map<std::string, std::string> paths;
  paths["source"] = std::string(argv[2]) + "/test-gx.bp";

  auto metaData = reader.ReadMetaData(paths);
  fides::metadata::Bool flag(false);
  metaData.Set(fides::keys::READ_AS_MULTIBLOCK(), flag);
  fides::metadata::Index step(0); // we want to read step 2
  metaData.Set(fides::keys::STEP_SELECTION(), step);

  vtkm::cont::PartitionedDataSet output = reader.ReadDataSet(paths, metaData);
  if (output.GetNumberOfPartitions() != 1)
  {
    std::cerr << "Error: expected 1 output block, got " << output.GetNumberOfPartitions()
              << std::endl;
    retVal = 1;
  }
  const vtkm::cont::DataSet& ds = output.GetPartition(0);

  if (ds.GetNumberOfCells() != 46109)
  {
    std::cerr << "Error: Expected 46109 cells, got: " << ds.GetNumberOfCells() << std::endl;
    retVal = 1;
  }
  if (ds.GetNumberOfPoints() != 46550)
  {
    std::cerr << "Error: Expected 46550 points, got: " << ds.GetNumberOfPoints() << std::endl;
    retVal = 1;
  }
  if (ds.GetNumberOfFields() != 8)
  {
    std::cerr << "Error: Expected 8 fields, got: " << ds.GetNumberOfFields() << std::endl;
    retVal = 1;
  }
  retVal = (CheckField<vtkm::Float64>(ds, "Lambda", { -0.321798, 0.321798 }, 1e-5) ? retVal : 1);
  retVal = (CheckField<vtkm::Float64>(ds, "nfp", { 5, 5 }, 1e-5) ? retVal : 1);
  retVal = (CheckField<vtkm::Float64>(ds, "num_surfaces", { 10, 10 }, 1e-5) ? retVal : 1);
  retVal = (CheckField<vtkm::Float64>(ds, "num_theta", { 50, 50 }, 1e-5) ? retVal : 1);
  retVal = (CheckField<vtkm::Float64>(ds, "num_zeta", { 20, 20 }, 1e-5) ? retVal : 1);
  retVal = (CheckField<vtkm::Float64>(ds, "SurfaceIndex", { 190, 199 }, 1e-5) ? retVal : 1);
  retVal = (CheckField<vtkm::Float64>(ds, "surface_min_index", { 190, 190 }, 1e-5) ? retVal : 1);
  if (writeVTK)
  {
    auto& outputData = output.GetPartition(0);
    outputData.PrintSummary(std::cout);
    vtkm::Id numcells = outputData.GetNumberOfCells();
    vtkm::io::VTKDataSetWriter writer("gx-output.vtk");
    writer.WriteDataSet(outputData);
  }
#ifdef FIDES_USE_MPI
  MPI_Finalize();
#endif
  return retVal;
}
