//=========================================================================
//  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.
//=========================================================================

#ifndef __smtk_simulation_ace3p_jobsManifest_h
#define __smtk_simulation_ace3p_jobsManifest_h
#include <string>

#include "nlohmann/json.hpp"

#include "smtk/simulation/ace3p/Exports.h"

namespace smtk
{
namespace simulation
{
namespace ace3p
{

// @brief builder for Job Records
// @long this acts as a form to be filled out, and then returns a valid json Job Record with as many fields filled out
//       as the user specified. Any requisite fields not filled out will be given placeholder values
struct JobRecordGenerator
{
  // constructor: fill out a blank Job Record
  JobRecordGenerator()
  {
    m_data = { { "job_id", "0" },
               { "job_name", "" },
               { "machine", "" },
               { "analysis", "" }, // <--- location of 'ACDTool' string, if present
               { "analysis_id", "" },
               { "nodes", "0" },
               { "processes", "0" },
               { "elapsed_time", "0" },
               { "status", "" },
               { "submission_time", "" },
               { "notes", "" },
               { "local_job_folder", "" },
               { "runtime_job_folder", "" },
               { "runtime_input_folder", "" },
               { "runtime_mesh_filename", "" },
               { "results_subfolder", "" },
               { "acdtool_task", "" } }; // new type here JOHN HERE STAN
  }
  // 'acdtool_task' will be set and time of job submission. Enumerate mapping from UI text to
  //                   json text in qtJobsWidget (likely a const static QMap<QString,std::string>)

  // Clear all values
  void reset()
  {
    for (auto& item : m_data.items())
    {
      item.value() = "";
    }
    this->elapsedTime(0);
    this->submissionTime("0");
    this->acdtoolTask("ACDTool"); // fallback text
  }

  // record the slurm ID of the job
  void jobID(std::string s) { m_data["job_id"] = s; }

  // record the user-specified name of the job
  void jobName(std::string s) { m_data["job_name"] = s; }

  // record the machine the job was submitted to
  void machine(std::string s) { m_data["machine"] = s; }

  // record the analyis of the job
  void analysis(std::string s) { m_data["analysis"] = s; }

  // record the UUID of the analysis progenitor this job
  void analysisID(std::string s) { m_data["analysis_id"] = s; }

  // record the number of nodes requested by the job
  void nodes(int s) { m_data["nodes"] = std::to_string(s); }
  void nodes(std::string s) { m_data["nodes"] = s; }

  // record the number of processes requested by the job
  void processes(int s) { m_data["processes"] = std::to_string(s); }
  void processes(std::string s) { m_data["processes"] = s; }

  // record the elapsed time (in seconds) of the job
  void elapsedTime(int s) { m_data["elapsed_time"] = std::to_string(s); }
  void elapsedTime(std::string s) { m_data["elapsed_time"] = s; }

  // record the submission timestamp (seconds since epoch)
  void submissionTime(std::string s) { m_data["submission_time"] = s; }

  // record the user-specified notes of the job
  void notes(std::string s) { m_data["notes"] = s; }

  // record the runtime folder the job was submitted from
  void runtimeJobFolder(std::string s) { m_data["runtime_job_folder"] = s; }

  // record the folder on the local filesystem for any job data
  void localJobFolder(std::string s) { m_data["local_job_folder"] = s; }

  // record the optional input folder specified with the job
  void runtimeInputFolder(std::string s) { m_data["runtime_input_folder"] = s; }

  // record the name of the mesh file used on the runtime machine
  void runtimeMeshFileName(std::string s) { m_data["runtime_mesh_filename"] = s; }

  // record the subdirectory name where results are written
  void resultsSubfolder(std::string s) { m_data["results_subfolder"] = s; }

  // record job status
  void status(std::string s) { m_data["status"] = s; }

  // record ACDTool task
  void acdtoolTask(std::string s) { m_data["acdtool_task"] = s; }

  // return the contained json Job Record
  nlohmann::json get() { return m_data; }

  // Job Record data stored in json format
  nlohmann::json m_data;
};

// represents a Jobs Manifest for a project
class SMTKACE3P_EXPORT JobsManifest
{
public:
  // @brief constructor
  JobsManifest() { m_data["Jobs"] = nlohmann::json::array(); }
  JobsManifest(nlohmann::json data);

  // @brief get a data entry from internal m_data by key.
  // return false if key not found (value will be set to empty string)
  bool getField(const int index, const std::string key, std::string& value) const;

  // @brief append a new Job Record to the manifest
  bool addJobRecord(nlohmann::json job);

  // @brief prepend a new Job Record to the manifest
  bool insertJobRecord(nlohmann::json job);

  // @brief find and remove Job Record from the manifest
  bool removeJobRecord(const std::string& jobId);

  // @brief write data to a job record field
  // @note changing the job ID will adjust the manifest index association
  bool setField(const int idx, const std::string key, const std::string value);

  // @brief write the jobs manifest to file
  bool write(std::string filename) const;

  // @brief read a jobs manifest from file
  bool read(std::string filename, bool& bChangedUponLoad);

  // @brief return the number of jobs in manifest
  size_t size() const { return m_data["Jobs"].size(); }

  // @brief get the underlying data structure
  nlohmann::json getData() const { return m_data; }

  // @brief get the manifest index of a job by its job ID
  // @note returns -1 if the id past in is not associated with an index
  int findIndex(const std::string& job_id) const;

  // @brief Returns true if the status string implies that the job has completed
  static bool isJobFinished(const std::string& status)
  {
    return (status == "complete") || (status == "error") || (status == "downloaded");
  }

protected:
  // set the internal data of the job manifest
  bool setInternalData(nlohmann::json data);

private:
  // @brief internal data storing jobs records
  nlohmann::json m_data;
};

} // namespace ace3p
} // namespace simulation
} // namespace smtk
#endif
