#include <ROOT/RNTupleWriter.hxx>
#include <ROOT/RNTupleReader.hxx>
#include <ROOT/RNTupleModel.hxx>

int main() {
  std::string_view TUPLENAME = "tuple";
  std::array<std::string_view, 3> FILEPATHS = {
    "multi-read-0.root",
    "multi-read-1.root",
    "multi-read-2.root"
  };

  {
    std::cout << "writing" << std::endl;

    for (int ifile{0}; ifile < FILEPATHS.size(); ifile++) {
      std::cout << "  " << FILEPATHS[ifile] << std::endl;
      if (ifile == 2) {
        // drop ifile column to test what happens when a field is missing
        auto model = ROOT::RNTupleModel::Create();
        auto value = model->MakeField<int>("value");
        auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), TUPLENAME, FILEPATHS[ifile]);
        for (int i{0}; i < 10; i++) {
          *value = i + 10*ifile;
          writer->Fill();
        }
      } else {
        auto model = ROOT::RNTupleModel::Create();
        auto value = model->MakeField<int>("value");
        auto ifile_ptr = model->MakeField<int>("ifile");
        auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), TUPLENAME, FILEPATHS[ifile]);
        *ifile_ptr = ifile;
        for (int i{0}; i < 10; i++) {
          *value = i + 10*ifile;
          writer->Fill();
        }
      }
    }
  }

  auto reading_model = ROOT::RNTupleModel::Create();
  {
    std::cout << "spy on first file to get model" << std::endl;
    ROOT::RNTupleDescriptor::RCreateModelOptions create_model_options;
    create_model_options.SetCreateBare(true);
    auto reader = ROOT::RNTupleReader::Open(create_model_options, TUPLENAME, FILEPATHS[0]);
    const auto& desc = reader->GetDescriptor();
    for (const auto& field_desc : desc.GetTopLevelFields()) {
      reading_model->AddField(field_desc.CreateField(desc));
    }
  }

  try {
    std::cout << "transfer values" << std::endl;
    reading_model->Freeze();
    auto ifile_ptr = reading_model->GetDefaultEntry().GetPtr<int>("ifile");
    auto value = reading_model->GetDefaultEntry().GetPtr<int>("value");
    for (int ifile{0}; ifile < FILEPATHS.size(); ifile++) {
      auto new_model = ROOT::RNTupleModel::Create();
      {
        ROOT::RNTupleDescriptor::RCreateModelOptions create_model_options;
        create_model_options.SetCreateBare(true);
        auto spy = ROOT::RNTupleReader::Open(create_model_options, TUPLENAME, FILEPATHS[ifile]);
        const auto& desc = spy->GetDescriptor();
        for (const auto& value : reading_model->GetDefaultEntry()) {
          std::string field_name{value.GetField().GetFieldName()};
          auto field_desc_id = desc.FindFieldId(field_name);
          if (field_desc_id == ROOT::kInvalidDescriptorId) {
            throw std::runtime_error("File does not have a field required to be read by model, file mismatch");
          }
          const auto& field_desc = desc.GetFieldDescriptor(field_desc_id);
          new_model->AddField(field_desc.CreateField(desc));
          new_model->GetDefaultEntry().BindValue<void>(field_name, value.GetPtr<void>());
        }
      }
      auto reader = ROOT::RNTupleReader::Open(std::move(new_model), TUPLENAME, FILEPATHS[ifile]);
      for (auto ientry : *reader) {
        reader->LoadEntry(ientry);
        std::cout << *ifile_ptr << " -> " << *value << std::endl;
      }
    }
  } catch (const ROOT::RException& e) {
    std::cout << "failed: " << e.what() << std::endl;
  } catch (const std::runtime_error& e) {
    std::cout << "caught mis-matched file: " << e.what() << std::endl;
  }
}
