#include "common/debug/GameDebugActor.h"
#include "character/CharacterObject.h"
#include "map/GameMap.h"
#include "ui/NineSliceActor.h"
#include <SDL2/SDL.h>
#include <algorithm>
#include <frostbite2D/2d/text_sprite.h>
#include <frostbite2D/scene/scene.h>
#include <limits>

namespace frostbite2D {

namespace {

constexpr float kDebugHudMarginX = 12.0f;
constexpr float kDebugHudMarginY = 12.0f;
constexpr float kDebugHudPaddingX = 8.0f;
constexpr float kDebugHudPaddingY = 6.0f;
constexpr float kDebugHudLineGap = 4.0f;
constexpr char kDebugHudPopupImg[] = "sprite/interface/newstyle/windows/popup/popup.img";

void ConfigureTextLine(RefPtr<TextSprite> textSprite, const char* name,
                       int zOrder) {
  if (!textSprite) {
    return;
  }
  textSprite->SetName(name);
  textSprite->SetFont("default");
  textSprite->SetTextColor(1.0f, 1.0f, 1.0f, 1.0f);
  textSprite->SetZOrder(zOrder);
}

} // namespace

GameDebugActor::GameDebugActor() {
  SetName("GameDebugActor");
  SetZOrder(std::numeric_limits<int>::max());
  initOverlay();
}

GameDebugActor& GameDebugActor::get() {
  return *getPtr();
}

Ptr<GameDebugActor> GameDebugActor::getPtr() {
  static Ptr<GameDebugActor> instance(new GameDebugActor());
  return instance;
}

bool GameDebugActor::AttachToScene(Scene* scene) {
  return AttachToParent(scene);
}

bool GameDebugActor::AttachToParent(Actor* parent) {
  if (!parent || parent == this) {
    return false;
  }

  Ptr<GameDebugActor> self = getPtr();
  if (GetParent() == parent) {
    return true;
  }

  if (GetParent()) {
    GetParent()->RemoveChild(self);
  }

  parent->AddChild(self);
  return GetParent() == parent;
}

void GameDebugActor::DetachFromParent() {
  Ptr<GameDebugActor> self = getPtr();
  if (GetParent()) {
    GetParent()->RemoveChild(self);
  }
}

void GameDebugActor::SetDebugMap(GameMap* map) {
  if (debugMap_ && debugMap_ != map) {
    debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex);
  }
  debugMap_ = map;
  updateOverlay();
}

void GameDebugActor::SetTrackedCharacter(CharacterObject* character) {
  trackedCharacter_ = character;
  updateOverlay();
}

void GameDebugActor::ClearDebugContext() {
  if (debugMap_) {
    debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex);
  }
  debugMap_ = nullptr;
  trackedCharacter_ = nullptr;
  setOverlayVisible(false);
}

void GameDebugActor::OnUpdate(float deltaTime) {
  (void)deltaTime;
  updateOverlay();
}

void GameDebugActor::initOverlay() {
  if (background_ || coordText_ || actionText_) {
    return;
  }

  background_ = NineSliceActor::createFromNpk(kDebugHudPopupImg, 0);
  if (background_) {
    background_->SetName("debugHudBackground");
    AddChild(background_);
  } else {
    SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
                "GameDebugActor: failed to load HUD background from %s",
                kDebugHudPopupImg);
  }

  coordText_ = TextSprite::create();
  ConfigureTextLine(coordText_, "debugCoordText", 1);
  AddChild(coordText_);

  actionText_ = TextSprite::create();
  ConfigureTextLine(actionText_, "debugActionText", 1);
  AddChild(actionText_);

  setOverlayVisible(false);
}

void GameDebugActor::updateOverlay() {
  if (!coordText_ || !actionText_) {
    setOverlayVisible(false);
    return;
  }

  updateMapDebugHighlight();
  if (!trackedCharacter_) {
    setOverlayVisible(false);
    return;
  }

  const CharacterWorldPosition& worldPosition = trackedCharacter_->GetWorldPosition();

  char coordTextBuffer[96];
  SDL_snprintf(coordTextBuffer, sizeof(coordTextBuffer), "角色坐标: (%d, %d, %d)",
               worldPosition.x, worldPosition.y, worldPosition.z);
  coordText_->SetText(coordTextBuffer);

  int frameCount = std::max(trackedCharacter_->GetCurrentAnimationFrameCount(), 1);
  int frameIndex = std::clamp(trackedCharacter_->GetCurrentAnimationFrameIndex(), 0,
                              frameCount - 1);
  char actionTextBuffer[128];
  SDL_snprintf(actionTextBuffer, sizeof(actionTextBuffer),
               "Action: %s dir:%c frame:%d/%d",
               trackedCharacter_->GetCurrentAction().empty()
                   ? "<none>"
                   : trackedCharacter_->GetCurrentAction().c_str(),
               trackedCharacter_->GetDirection() >= 0 ? 'R' : 'L', frameIndex + 1,
               frameCount);
  actionText_->SetText(actionTextBuffer);

  Vec2 coordTextSize = coordText_->GetTextSize();
  Vec2 actionTextSize = actionText_->GetTextSize();
  float panelWidth = std::max(coordTextSize.x, actionTextSize.x) + kDebugHudPaddingX * 2.0f;

  float contentHeight = coordTextSize.y;
  if (actionText_->IsVisible()) {
    contentHeight += kDebugHudLineGap + actionTextSize.y;
  }

  float panelHeight = contentHeight + kDebugHudPaddingY * 2.0f;
  Vec2 panelSize(panelWidth, panelHeight);
  if (background_) {
    background_->SetSize(panelSize);
  }

  SetPosition(kDebugHudMarginX, kDebugHudMarginY);
  SetScale(1.0f);

  float currentY = kDebugHudPaddingY;
  coordText_->SetPosition(kDebugHudPaddingX, currentY);
  currentY += coordTextSize.y;

  if (actionText_->IsVisible()) {
    currentY += kDebugHudLineGap;
    actionText_->SetPosition(kDebugHudPaddingX, currentY);
    currentY += actionTextSize.y;
  }

  setOverlayVisible(true);
}

void GameDebugActor::updateMapDebugHighlight() {
  if (!debugMap_) {
    return;
  }

  if (!trackedCharacter_ || !debugMap_->IsDebugModeEnabled()) {
    debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex);
    return;
  }

  const CharacterWorldPosition& worldPosition = trackedCharacter_->GetWorldPosition();
  Vec3 currentWorldPos(static_cast<float>(worldPosition.x),
                       static_cast<float>(worldPosition.y),
                       static_cast<float>(worldPosition.z));
  size_t moveAreaIndex = debugMap_->FindMoveAreaIndex(currentWorldPos);
  debugMap_->SetDebugHighlightedMoveAreaIndex(moveAreaIndex);
}

void GameDebugActor::setOverlayVisible(bool visible) {
  if (background_) {
    background_->SetVisible(visible);
  }
  if (coordText_) {
    coordText_->SetVisible(visible);
  }
  if (actionText_) {
    actionText_->SetVisible(visible);
  }
}

} // namespace frostbite2D
