/**
 * @file jsonPorter.h
 */

#pragma once

#include <fstream>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>

#include "nlohmann/json.hpp"

namespace arch {

// Forward declared dependencies
template<typename T>
class Network;

}   // namespace arch

namespace sim {

// Forward declared dependencies
template<typename T>
class Simulation;

}   // namespace sim

namespace porting {

/**
 * @brief Constructor of the Network from a JSON file
 * @param[in] jsonFile Location of the json file
 * @returns Network network
*/
template<typename T>
std::shared_ptr<arch::Network<T>> networkFromJSON(std::string jsonFile);

/**
 * @brief Define an existing Network from a JSON file
 * @param[in] jsonFile location of the json file
 * @param[in] network network object that needs to be defined
 * @returns Network network
*/
template<typename T>
void networkFromJSON(std::string jsonFile, std::shared_ptr<arch::Network<T>>& network);

/**
 * @brief Constructor of the Network from a JSON string
 * @param[in] json json string
 * @returns Network network
*/
template<typename T>
std::shared_ptr<arch::Network<T>> networkFromJSON(nlohmann::json jsonString);

/**
 * @brief Constructor of the Simulation from a JSON file
 * @param[in] jsonFile json file
 * @param[in] network pointer to the network on which the simulation acts
 * @returns unique_ptr<sim::Simulation<T>> simulation
*/
template<typename T>
std::unique_ptr<sim::Simulation<T>> simulationFromJSON(std::string jsonFile, std::shared_ptr<arch::Network<T>> network);

/**
 * @brief Constructor of the Simulation from a JSON string
 * @param[in] json json string
 * @param[in] network pointer to the network on which the simulation acts
 * @returns unique_ptr<sim::Simulation<T>> simulation
*/
template<typename T>
std::unique_ptr<sim::Simulation<T>> simulationFromJSON(nlohmann::json jsonString, std::shared_ptr<arch::Network<T>> network);

/**
 * @brief Generates a json string of the simulation results and writes it in the provided location
 * @param[in] jsonFile location at which the json string should be written
 * @param[in] simulation pointer to the simulation of which the results must be stored
*/
template<typename T>
void resultToJSON(std::string jsonFile, sim::Simulation<T>* simulation);

/**
 * @brief Generates a json string of the simulation results and writes it in the provided location
 * @param[in] jsonFile location at which the json string should be written
 * @param[in] simulation pointer to the simulation of which the results must be stored
*/
template<typename T>
inline void resultToJSON(std::string jsonFile, const std::unique_ptr<sim::Simulation<T>>& simulation) { resultToJSON(jsonFile, simulation.get()); }

/**
 * @brief Constructor of a json string of the simulation results
 * @param[in] simulation pointer to the simulation of which the results must be stored
 * @returns ordered_json json string
*/
template<typename T>
nlohmann::ordered_json resultToJSON(sim::Simulation<T>* simulation);

/**
 * @brief Constructor of a json string of the simulation results
 * @param[in] simulation pointer to the simulation of which the results must be stored
 * @returns ordered_json json string
*/
template<typename T>
inline nlohmann::ordered_json resultToJSON(const std::unique_ptr<sim::Simulation<T>>& simulation) { return resultToJSON(simulation.get()); }

}   // namespace porting