/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2025 Xenia Canary. All rights reserved.                          *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/kernel/xam/ui/game_achievements_ui.h"

namespace xe {
namespace kernel {
namespace xam {
namespace ui {

GameAchievementsUI::GameAchievementsUI(xe::ui::ImGuiDrawer* imgui_drawer,
                                       const ImVec2 drawing_position,
                                       const TitleInfo* title_info,
                                       const UserProfile* profile)
    : XamDialog(imgui_drawer),
      drawing_position_(drawing_position),
      title_info_(*title_info),
      profile_(profile),
      window_id_(GetWindowId()) {
  LoadAchievementsData();
}

GameAchievementsUI::~GameAchievementsUI() {
  for (auto& entry : achievements_icons_) {
    entry.second.release();
  }
}

bool GameAchievementsUI::LoadAchievementsData() {
  achievements_info_ =
      kernel_state()->xam_state()->achievement_manager()->GetTitleAchievements(
          profile_->xuid(), title_info_.id);

  if (achievements_info_.empty()) {
    return false;
  }

  xe::ui::IconsData data;

  for (const Achievement& entry : achievements_info_) {
    const auto icon =
        kernel_state()->xam_state()->achievement_manager()->GetAchievementIcon(
            profile_->xuid(), title_info_.id, entry.achievement_id);

    data.insert({entry.image_id, icon});
  }

  achievements_icons_ = imgui_drawer()->LoadIcons(data);
  return true;
}

std::string GameAchievementsUI::GetAchievementTitle(
    const Achievement& achievement_entry) const {
  std::string title = "Secret trophy";

  if (achievement_entry.IsUnlocked() || show_locked_info_ ||
      achievement_entry.flags &
          static_cast<uint32_t>(AchievementFlags::kShowUnachieved)) {
    title = xe::to_utf8(achievement_entry.achievement_name);
  }

  return title;
}

std::string GameAchievementsUI::GetAchievementDescription(
    const Achievement& achievement_entry) const {
  std::string description = "Hidden description";

  if (achievement_entry.flags &
      static_cast<uint32_t>(AchievementFlags::kShowUnachieved)) {
    description = xe::to_utf8(achievement_entry.locked_description);
  }

  if (achievement_entry.IsUnlocked() || show_locked_info_) {
    description = xe::to_utf8(achievement_entry.unlocked_description);
  }

  return description;
}

xe::ui::ImmediateTexture* GameAchievementsUI::GetIcon(
    const Achievement& achievement_entry) const {
  if (!achievement_entry.IsUnlocked() && !show_locked_info_) {
    return imgui_drawer()->GetLockedAchievementIcon();
  }

  if (achievements_icons_.count(achievement_entry.image_id)) {
    return achievements_icons_.at(achievement_entry.image_id).get();
  }

  if (achievement_entry.IsUnlocked()) {
    return nullptr;
  }
  return imgui_drawer()->GetLockedAchievementIcon();
}

std::string GameAchievementsUI::GetUnlockedTime(
    const Achievement& achievement_entry) const {
  if (achievement_entry.IsUnlockedOnline()) {
    const auto unlock_time =
        std::chrono::system_clock::to_time_t(chrono::WinSystemClock::to_sys(
            achievement_entry.unlock_time.to_time_point()));

    return fmt::format("Unlocked: Online {:%Y-%m-%d %H:%M}",
                       *std::localtime(&unlock_time));
  }

  if (achievement_entry.unlock_time.is_valid()) {
    const auto unlock_time =
        std::chrono::system_clock::to_time_t(chrono::WinSystemClock::to_sys(
            achievement_entry.unlock_time.to_time_point()));

    return fmt::format("Unlocked: Offline ({:%Y-%m-%d %H:%M})",
                       *std::localtime(&unlock_time));
  }
  return fmt::format("Unlocked: Offline");
}

void GameAchievementsUI::DrawTitleAchievementInfo(
    ImGuiIO& io, const Achievement& achievement_entry) const {
  const auto start_drawing_pos = ImGui::GetCursorPos();

  ImGui::TableSetColumnIndex(0);

  const auto icon = GetIcon(achievement_entry);
  if (icon) {
    ImGui::Image(reinterpret_cast<ImTextureID>(GetIcon(achievement_entry)),
                 xe::ui::default_image_icon_size);
  } else {
    ImGui::Dummy(xe::ui::default_image_icon_size);
  }
  ImGui::TableNextColumn();

  ImGui::PushFont(imgui_drawer()->GetTitleFont());
  const auto primary_line_height = ImGui::GetTextLineHeight();
  ImGui::Text("%s", GetAchievementTitle(achievement_entry).c_str());
  ImGui::PopFont();

  ImGui::PushTextWrapPos(ImGui::GetMainViewport()->Size.x * 0.5f);
  ImGui::TextWrapped("%s",
                     GetAchievementDescription(achievement_entry).c_str());
  ImGui::PopTextWrapPos();

  ImGui::SetCursorPosY(start_drawing_pos.y + xe::ui::default_image_icon_size.x -
                       ImGui::GetTextLineHeight());

  if (achievement_entry.IsUnlocked()) {
    ImGui::Text("%s", GetUnlockedTime(achievement_entry).c_str());
  }

  ImGui::TableNextColumn();

  // TODO(Gliniak): There is no easy way to align text to middle, so I have to
  // do it manually.
  const float achievement_row_middle_alignment =
      ((xe::ui::default_image_icon_size.x / 2.f) -
       ImGui::GetTextLineHeight() / 2.f) *
      0.85f;

  ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
                       achievement_row_middle_alignment);
  ImGui::PushFont(imgui_drawer()->GetTitleFont());
  ImGui::TextUnformatted(
      fmt::format("{} G", achievement_entry.gamerscore).c_str());
  ImGui::PopFont();
}

void GameAchievementsUI::OnDraw(ImGuiIO& io) {
  ImGui::SetNextWindowPos(drawing_position_, ImGuiCond_FirstUseEver);

  const auto xenia_window_size = ImGui::GetMainViewport()->Size;

  ImGui::SetNextWindowSizeConstraints(
      ImVec2(xenia_window_size.x * 0.2f, xenia_window_size.y * 0.3f),
      ImVec2(xenia_window_size.x * 0.6f, xenia_window_size.y * 0.8f));
  ImGui::SetNextWindowBgAlpha(0.8f);

  bool dialog_open = true;

  std::string title_name = xe::to_utf8(title_info_.title_name);
  title_name.erase(std::remove(title_name.begin(), title_name.end(), '\0'),
                   title_name.end());

  const std::string window_name =
      fmt::format("{} Achievements###{}", title_name, window_id_);
  if (!ImGui::Begin(window_name.c_str(), &dialog_open,
                    ImGuiWindowFlags_NoCollapse |
                        ImGuiWindowFlags_AlwaysAutoResize |
                        ImGuiWindowFlags_HorizontalScrollbar)) {
    Close();
    ImGui::End();
    return;
  }

  ImGui::Checkbox("Show locked achievements information", &show_locked_info_);
  ImGui::Separator();

  if (achievements_info_.empty()) {
    ImGui::TextUnformatted(fmt::format("No achievements data!").c_str());
  } else {
    if (ImGui::BeginTable("", 3, ImGuiTableFlags_BordersInnerH)) {
      for (const auto& entry : achievements_info_) {
        ImGui::TableNextRow(0, xe::ui::default_image_icon_size.y);
        DrawTitleAchievementInfo(io, entry);
      }

      ImGui::EndTable();
    }
  }

  if (!dialog_open) {
    Close();
    ImGui::End();
    return;
  }

  ImGui::End();
};

}  // namespace ui
}  // namespace xam
}  // namespace kernel
}  // namespace xe
