//============================================================================
//  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>
#include <fides/predefined/DataModelFactory.h>
#include <fides/predefined/InternalMetadataSource.h>
#include <fides/predefined/SupportedDataModels.h>

#include <fides_rapidjson.h>
// clang-format off
#include FIDES_RAPIDJSON(rapidjson/error/en.h)
#include FIDES_RAPIDJSON(rapidjson/filereadstream.h)
#include FIDES_RAPIDJSON(rapidjson/prettywriter.h)
#include FIDES_RAPIDJSON(rapidjson/schema.h)
#include FIDES_RAPIDJSON(rapidjson/stringbuffer.h)
// clang-format on

#include "SchemaValidator.h"

#ifdef FIDES_USE_MPI
#include <mpi.h>
#endif

// matches order of DataModelTypes
std::vector<std::string> bpFiles = { "cartesian-attr.bp",
                                     "rectilinear-attr.bp",
                                     "tris-explicit-attr.bp",
                                     "tris-blocks-time-attr.bp",
                                     "xgc-attr.bp" };

void testReader(const std::string& dir, const std::string& dataModelStr)
{
  auto dataModel = fides::predefined::ConvertDataModelToEnum(dataModelStr);
  std::string filename = dir + "/" + bpFiles[static_cast<size_t>(dataModel)];
  auto rc = fides::io::DataSetReader::CheckForDataModelAttribute(filename);
  if (dataModel != fides::predefined::DataModelTypes::UNSUPPORTED && !rc)
  {
    throw std::runtime_error("Data model " + dataModelStr +
                             " does not appear to be a supported "
                             "predefined data model");
  }

  std::unordered_map<std::string, std::string> paths;
  if (dataModel == fides::predefined::DataModelTypes::XGC)
  {
    paths["3d"] = dir + "/";
    paths["mesh"] = dir + "/";
    paths["diag"] = dir + "/";
  }
  else
  {
    paths["source"] = filename;
  }
  fides::io::DataSetReader reader(filename, fides::io::DataSetReader::DataModelInput::BPFile);
  auto metadata = reader.ReadMetaData(paths);

  reader.PrepareNextStep(paths);
  fides::metadata::MetaData selections;
  auto output = reader.ReadDataSet(paths, selections);
}

void validateSchema(const std::string& dir,
                    const std::string& dataModelStr,
                    const std::string& schemaFile)
{
  auto dataModel = fides::predefined::ConvertDataModelToEnum(dataModelStr);
  std::string filename = dir + "/" + bpFiles[static_cast<size_t>(dataModel)];
  std::shared_ptr<fides::predefined::InternalMetadataSource> metadataSource(
    new fides::predefined::InternalMetadataSource(filename));
  auto dm = fides::predefined::DataModelFactory::GetInstance().CreateDataModel(metadataSource);
  auto& doc = dm->GetDOM(true);
  rapidjson::StringBuffer buffer;
  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  doc.Accept(writer);

  // run through validator
  SchemaValidator validator(schemaFile);
  if (!validator.SetDocument(buffer.GetString()))
  {
    throw std::runtime_error("Converting JSON string to document failed");
  }

  if (!validator.Validate())
  {
    throw std::runtime_error("Validation failed");
  }
}

int main(int argc, char** argv)
{
#ifdef FIDES_USE_MPI
  MPI_Init(&argc, &argv);
#endif

  if (argc != 4)
  {
    std::cerr << "Usage: " << argv[0]
              << "predefined-model-name /path/to/dataroot fides-schema.json\n";
    return 1;
  }

  std::string dataModelStr = argv[1];
  auto dataModel = fides::predefined::ConvertDataModelToEnum(dataModelStr);
  switch (dataModel)
  {
    case fides::predefined::DataModelTypes::UNIFORM:
      testReader(argv[2], dataModelStr);
      validateSchema(argv[2], dataModelStr, argv[3]);
      break;
    case fides::predefined::DataModelTypes::RECTILINEAR:
      testReader(argv[2], dataModelStr);
      validateSchema(argv[2], dataModelStr, argv[3]);
      break;
    case fides::predefined::DataModelTypes::UNSTRUCTURED:
      testReader(argv[2], dataModelStr);
      validateSchema(argv[2], dataModelStr, argv[3]);
      break;
    case fides::predefined::DataModelTypes::UNSTRUCTURED_SINGLE:
      testReader(argv[2], dataModelStr);
      validateSchema(argv[2], dataModelStr, argv[3]);
      break;
    case fides::predefined::DataModelTypes::XGC:
      testReader(argv[2], dataModelStr);
      validateSchema(argv[2], dataModelStr, argv[3]);
      break;
    default:
      throw std::runtime_error("Not a supported data model");
  }

#ifdef FIDES_USE_MPI
  MPI_Finalize();
#endif
  return 0;
}
