#include "domain/layout_profile.hpp"

#include "util/id_util.hpp"
#include "util/string_util.hpp"

namespace lm {

nlohmann::json LayoutProfile::make(const std::string& name,
                                const nlohmann::json& layout,
                                const nlohmann::json& lidars,
                                const nlohmann::json& imus)
{
  const std::string ts = IdUtil::nowIso8601();
  return nlohmann::json{{"id", IdUtil::newId()},
              {"name", name},
              {"created_at", ts},
              {"updated_at", ts},
              {"layout", layout},
              {"lidars", lidars},
              {"imus", imus}};
}

void LayoutProfile::touch(nlohmann::json& profile)
{
  profile["updated_at"] = IdUtil::nowIso8601();
}

std::string LayoutProfile::modelFromLayout(const nlohmann::json& layout)
{
  if (layout.is_object() && layout.contains("robot") && layout["robot"].is_object() &&
      layout["robot"].contains("model") && layout["robot"]["model"].is_string())
    return layout["robot"]["model"].get<std::string>();
  return "diff";
}

nlohmann::json LayoutProfile::catalogEntryFromProfile(const nlohmann::json& profile)
{
  const nlohmann::json& layout = profile.contains("layout") ? profile["layout"] : nlohmann::json::object();
  const size_t lidar_count =
    profile.contains("lidars") && profile["lidars"].is_array() ? profile["lidars"].size() : 0;
  const size_t imu_count =
    profile.contains("imus") && profile["imus"].is_array() ? profile["imus"].size() : 0;
  return nlohmann::json{{"id", profile["id"]},
              {"name", profile["name"]},
              {"model", modelFromLayout(layout)},
              {"created_at", profile.value("created_at", "")},
              {"updated_at", profile.value("updated_at", "")},
              {"lidar_count", lidar_count},
              {"imu_count", imu_count}};
}

std::optional<size_t> LayoutProfile::findIndex(const nlohmann::json& state, const std::string& id)
{
  if (!state.contains("layouts") || !state["layouts"].is_array())
    return std::nullopt;
  const auto& layouts = state["layouts"];
  for (size_t i = 0; i < layouts.size(); i++)
  {
    const auto& p = layouts[i];
    if (p.is_object() && p.contains("id") && p["id"].is_string() && p["id"].get<std::string>() == id)
      return i;
  }
  return std::nullopt;
}

std::optional<size_t> LayoutProfile::findActiveIndex(nlohmann::json& state)
{
  if (!state.contains("active_layout_id") || !state["active_layout_id"].is_string())
    return std::nullopt;
  return findIndex(state, state["active_layout_id"].get<std::string>());
}

bool LayoutProfile::nameExists(const nlohmann::json& state,
                               const std::string& name,
                               const std::string* exclude_id)
{
  if (!state.contains("layouts") || !state["layouts"].is_array())
    return false;
  const std::string n = StringUtil::trimCopy(name);
  for (const auto& p : state["layouts"])
  {
    if (!p.is_object() || !p.contains("name"))
      continue;
    if (exclude_id && p.contains("id") && p["id"].get<std::string>() == *exclude_id)
      continue;
    if (StringUtil::trimCopy(p["name"].get<std::string>()) == n)
      return true;
  }
  return false;
}

nlohmann::json LayoutProfile::buildCatalog(const nlohmann::json& state)
{
  nlohmann::json catalog = nlohmann::json::array();
  if (!state.contains("layouts") || !state["layouts"].is_array())
    return catalog;
  for (const auto& p : state["layouts"])
  {
    if (!p.is_object() || !p.contains("id") || !p.contains("name"))
      continue;
    catalog.push_back(p);
  }
  return catalog;
}
}  // namespace lm
