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

// local includes
#include "JobsManifest.h"

// stl includes
#include <fstream>
#include <iostream>

namespace smtk
{
namespace simulation
{
namespace ace3p
{

JobsManifest::JobsManifest(nlohmann::json data)
{
  setInternalData(data);
}

//-------------------------------------------------------------------------
bool JobsManifest::setInternalData(nlohmann::json data)
{
  // TODO add data checking to make sure data is valid
  if (!data["Jobs"].is_array())
  {
    std::cerr << "Jobs Manifest contains no array Jobs of Job Records" << std::endl;
    return false;
  }

  m_data = data["Jobs"];
  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::addJobRecord(nlohmann::json job)
{
  m_data["Jobs"].insert(m_data["Jobs"].begin(), job);
  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::insertJobRecord(nlohmann::json job)
{
  m_data["Jobs"].insert(m_data["Jobs"].begin(), job);
  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::removeJobRecord(const std::string& jobId)
{
  int jobIndex = this->findIndex(jobId);
  m_data["Jobs"].erase(jobIndex);
  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::setField(const int idx, const std::string key, const std::string value)
{
  m_data["Jobs"][idx][key] = value;
  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::write(std::string filename) const
{
  std::string fileContents = m_data.dump(2);
  std::ofstream file(filename);
  file << fileContents;
  file.close();
  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::read(std::string filename, bool& bChangedUponLoad)
{
  std::ifstream file(filename);
  if (!file.good())
  {
    std::cerr << "Cannot read file \"" << filename << "\".\n";
    file.close();
    return false;
  }

  try
  {
    m_data = nlohmann::json::parse(/*"{ \"Jobs\": [] }"*/ file);
  }
  catch (...)
  {
    std::cerr << "Cannot parse file \"" << filename << "\".\n";
    file.close();
    return false;
  }

  file.close();

  if (!m_data.contains("Jobs"))
  {
    m_data = { "Jobs", {} };
    std::cout << "Warning: JobsManifest is missing Jobs array.";
  }

  nlohmann::json jJobs = m_data["Jobs"];

  // modernize JSON to remove old key strings (if needed)
  bChangedUponLoad = false;
  for (auto it = jJobs.begin(); it != jJobs.end(); ++it)
  {
    nlohmann::json entry = *it;
    if (entry.contains("slurm_id"))
    {
      entry["job_id"] = entry["slurm_id"].get<std::string>();
      entry.erase("slurm_id");
      entry.erase("cumulus_id");
      entry.erase("cumulus_folder_id");
      entry["acdtool_task"] = "ACDTool";
      bChangedUponLoad = true; // indicate that it is necessary to save manifest
    }
    else
    {
      break;
    }
    jJobs.erase(it);
    jJobs.insert(it, entry);
  }

  if (bChangedUponLoad)
  {
    m_data["Jobs"] = jJobs;
  }

  return true;
}

//-------------------------------------------------------------------------
bool JobsManifest::getField(const int index, const std::string key, std::string& value) const
{
  nlohmann::json jArray = m_data["Jobs"];
  if (index >= jArray.size())
  {
    std::cerr << __FILE__ << ":" << __LINE__ << " invalid index: " << index << std::endl;
    value = "?";
    return false;
  }
  nlohmann::json jRecord = jArray[index];
  auto iter = jRecord.find(key);
  if (iter == jRecord.end())
  {
    std::cerr << __FILE__ << ":" << __LINE__ << " "
              << "index " << index << " missing key: " << key << std::endl;
    value = "?";
    return false;
  }

  value = iter->get<std::string>();
  return true;
}

int JobsManifest::findIndex(const std::string& job_id) const
{
  auto jobsArray = m_data["Jobs"];
  auto iter = jobsArray.cbegin();
  for (int i = 0; iter != jobsArray.cend(); ++i, ++iter)
  {
    if (iter->at("job_id").get<std::string>() == job_id)
    {
      return i;
    }
  }

  return -1; // not found
}

} // namespace ace3p
} // namespace simulation
} // namespace smtk
