#include <frostbite2D/2d/actor.h>
#include <frostbite2D/base/object_registry.h>
#include <frostbite2D/event/key_event.h>
#include <frostbite2D/event/mouse_event.h>
#include <frostbite2D/event/touch_event.h>
#include <frostbite2D/event/joystick_event.h>
#include <frostbite2D/event/window_event.h>
#include <SDL2/SDL.h>
#include <algorithm>

namespace frostbite2D {

Actor::Actor()
  : parent_(nullptr)
  , scene_(nullptr)
  , rotation_(0.0f)
  , visible_(true)
  , zOrder_(0)
  , opacity_(1.0f)
  , anchor_(Vec2(0.0f, 0.0f)) {
  uuid_ = UUID::generate();
  ObjectRegistry::get().registerObject(uuid_, this);
}

void Actor::SetOpacity(float opacity) {
  opacity_ = std::clamp(opacity, 0.0f, 1.0f);
}

float Actor::GetWorldOpacity() const {
  float worldOpacity = opacity_;
  if (parent_) {
    worldOpacity *= parent_->GetWorldOpacity();
  }
  return worldOpacity;
}

void Actor::SetPosition(const Vec2& pos) {
  position_ = pos;
  markTransformDirty();
}

void Actor::SetPosition(float x, float y) {
  position_ = Vec2(x, y);
  markTransformDirty();
}

void Actor::SetRotation(float rotation) {
  rotation_ = rotation;
  markTransformDirty();
}

void Actor::SetScale(const Vec2& scale) {
  scale_ = scale;
  markTransformDirty();
}

void Actor::SetScale(float scale) {
  scale_ = Vec2(scale, scale);
  markTransformDirty();
}

void Actor::SetSkew(const Vec2& skew) {
  skew_ = skew;
  markTransformDirty();
}

void Actor::SetSkew(float skewX, float skewY) {
  skew_ = Vec2(skewX, skewY);
  markTransformDirty();
}

void Actor::SetSize(const Vec2& size) {
  size_ = size;
  markTransformDirty();
}

void Actor::SetSize(float width, float height) {
  size_ = Vec2(width, height);
  markTransformDirty();
}

void Actor::SetAnchor(const Vec2& anchor) {
  anchor_ = anchor;
  markTransformDirty();
}

void Actor::SetAnchor(float x, float y) {
  anchor_ = Vec2(x, y);
  markTransformDirty();
}

Vec2 Actor::GetTopLeftPosition() const {
  return Vec2(position_.x - anchor_.x * size_.x,
              position_.y - anchor_.y * size_.y);
}

void Actor::SetTopLeftPosition(const Vec2& pos) {
  position_ = Vec2(pos.x + anchor_.x * size_.x,
                   pos.y + anchor_.y * size_.y);
  markTransformDirty();
}

void Actor::SetTopLeftPosition(float x, float y) {
  SetTopLeftPosition(Vec2(x, y));
}

Vec2 Actor::LocalToWorld(const Vec2& point) const {
  return GetWorldTransform().transformPoint(point);
}

Vec2 Actor::WorldToLocal(const Vec2& point) const {
  return GetWorldTransform().inverse().transformPoint(point);
}

Rect Actor::GetWorldBounds() const {
  Vec2 topLeft = LocalToWorld(Vec2(0.0f, 0.0f));
  Vec2 topRight = LocalToWorld(Vec2(size_.x, 0.0f));
  Vec2 bottomLeft = LocalToWorld(Vec2(0.0f, size_.y));
  Vec2 bottomRight = LocalToWorld(Vec2(size_.x, size_.y));

  float minX = std::min(std::min(topLeft.x, topRight.x),
                        std::min(bottomLeft.x, bottomRight.x));
  float maxX = std::max(std::max(topLeft.x, topRight.x),
                        std::max(bottomLeft.x, bottomRight.x));
  float minY = std::min(std::min(topLeft.y, topRight.y),
                        std::min(bottomLeft.y, bottomRight.y));
  float maxY = std::max(std::max(topLeft.y, topRight.y),
                        std::max(bottomLeft.y, bottomRight.y));

  return Rect(minX, minY, maxX - minX, maxY - minY);
}

bool Actor::ContainsWorldPoint(const Vec2& point) const {
  return GetWorldBounds().containsPoint(point);
}

bool Actor::ContainsWorldPoint(float x, float y) const {
  return ContainsWorldPoint(Vec2(x, y));
}

const Transform2D& Actor::GetLocalTransform() const {
  if (transformDirty_) {
    updateLocalTransform();
  }
  return localTransform_;
}

const Transform2D& Actor::GetWorldTransform() const {
  if (transformDirty_) {
    updateLocalTransform();
    updateWorldTransform();
    transformDirty_ = false;
  }
  return worldTransform_;
}

void Actor::markTransformDirty() {
  transformDirty_ = true;
  OnTransformChanged();
  for (auto it = children_.begin(); it != children_.end(); ++it) {
    if (*it) {
      (*it)->markTransformDirty();
    }
  }
}

void Actor::updateLocalTransform() const {
  Vec2 anchorOffset = Vec2(anchor_.x * size_.x, anchor_.y * size_.y);
  localTransform_ = Transform2D::translation(position_.x, position_.y) *
                    Transform2D::rotation(rotation_) *
                    Transform2D::skewing(skew_.x, skew_.y) *
                    Transform2D::scaling(scale_.x, scale_.y) *
                    Transform2D::translation(-anchorOffset.x, -anchorOffset.y);
}

void Actor::updateWorldTransform() const {
  if (parent_) {
    worldTransform_ = parent_->GetWorldTransform() * localTransform_;
  } else {
    worldTransform_ = localTransform_;
  }
}

Actor::~Actor() {
  ObjectRegistry::get().unregisterObject(uuid_);
  RemoveAllChildren();
}

void Actor::Update(float deltaTime) {
  executeUpdateListeners(UpdatePhase::BeforeChildren, deltaTime);
  OnUpdate(deltaTime);
  UpdateChildren(deltaTime);
  executeUpdateListeners(UpdatePhase::AfterChildren, deltaTime);
}

void Actor::Render() {
  RenderChildren();
}

void Actor::insertChildByZOrder(Ptr<Actor> child) {
  if (!child) {
    return;
  }

  if (children_.IsEmpty()) {
    children_.PushBack(child);
    return;
  }

  auto it = children_.begin();
  while (it != children_.end()) {
    if (*it && (*it)->GetZOrder() > child->GetZOrder()) {
      children_.InsertBefore(child, *it);
      return;
    }
    ++it;
  }

  children_.PushBack(child);
}

void Actor::AddChild(Ptr<Actor> child) {
  if (!child) {
    return;
  }

  if (child->GetParent() == this) {
    return;
  }

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

  child->SetParent(this);
  child->SetScene(scene_);
  if (iteratingChildren_) {
    // 正在遍历孩子时不能立即重新插入排序位置，否则会破坏链表迭代状态。
    children_.PushBack(child);
    markChildrenOrderDirty();
  } else {
    insertChildByZOrder(child);
  }
  child->OnAdded(this);
}

void Actor::SetZOrder(int zOrder) {
  if (zOrder_ == zOrder) {
    return;
  }

  zOrder_ = zOrder;

  if (parent_) {
    // 不再在这里直接 RemoveChild/AddChild，避免更新阶段改动正在遍历的链表。
    parent_->markChildrenOrderDirty();
  }
}

void Actor::RemoveChild(Ptr<Actor> child) {
  if (!child || child->GetParent() != this) {
    return;
  }

  child->SetParent(nullptr);
  child->SetScene(nullptr);
  children_.Remove(child);
}

void Actor::RemoveAllChildren() {
  while (!children_.IsEmpty()) {
    Ptr<Actor> child = children_.GetFirst();
    RemoveChild(child);
  }
}

void Actor::UpdateChildren(float deltaTime) {
  iteratingChildren_ = true;
  for (auto it = children_.begin(); it != children_.end(); ++it) {
    if (*it && (*it)->IsVisible()) {
      (*it)->Update(deltaTime);
    }
  }
  iteratingChildren_ = false;

  if (childrenOrderDirty_) {
    reorderChildrenByZOrder();
  }
}

void Actor::RenderChildren() {
  if (childrenOrderDirty_ && !iteratingChildren_) {
    reorderChildrenByZOrder();
  }

  iteratingChildren_ = true;
  for (auto it = children_.begin(); it != children_.end(); ++it) {
    if (*it && (*it)->IsVisible()) {
      (*it)->Render();
    }
  }
  iteratingChildren_ = false;
}

void Actor::markChildrenOrderDirty() {
  childrenOrderDirty_ = true;
}

void Actor::reorderChildrenByZOrder() {
  if (!childrenOrderDirty_) {
    return;
  }

  // 统一在安全时机按 zOrder 重建顺序，解决上下移动时频繁改层级导致的卡死。
  std::vector<Ptr<Actor>> orderedChildren;
  for (auto it = children_.begin(); it != children_.end(); ++it) {
    if (*it) {
      orderedChildren.push_back(*it);
    }
  }

  std::sort(orderedChildren.begin(), orderedChildren.end(),
            [](const Ptr<Actor>& lhs, const Ptr<Actor>& rhs) {
              if (!lhs || !rhs) {
                return static_cast<bool>(lhs);
              }
              return lhs->GetZOrder() < rhs->GetZOrder();
            });

  while (!children_.IsEmpty()) {
    Ptr<Actor> child = children_.GetFirst();
    children_.Remove(child);
  }

  for (auto& child : orderedChildren) {
    children_.PushBack(child);
  }

  childrenOrderDirty_ = false;
}

uint32 Actor::AddUpdateListener(UpdateCallback callback, UpdatePhase phase) {
  if (!callback) {
    return 0;
  }

  UpdateListener listener;
  listener.id = nextUpdateListenerId_++;
  listener.phase = phase;
  listener.callback = std::move(callback);
  updateListeners_.push_back(std::move(listener));
  return updateListeners_.back().id;
}

bool Actor::RemoveUpdateListener(uint32 listenerId) {
  for (auto& listener : updateListeners_) {
    if (listener.id == listenerId && listener.active) {
      listener.active = false;
      if (iteratingUpdateListeners_) {
        updateListenersDirty_ = true;
      } else {
        compactUpdateListeners();
      }
      return true;
    }
  }
  return false;
}

void Actor::ClearUpdateListeners() {
  if (iteratingUpdateListeners_) {
    for (auto& listener : updateListeners_) {
      listener.active = false;
    }
    updateListenersDirty_ = true;
    return;
  }

  updateListeners_.clear();
}

uint32 Actor::AddEventListener(EventType type, EventCallback callback) {
  EventListener listener;
  listener.id = nextListenerId_++;
  listener.type = type;
  listener.callback = std::move(callback);
  eventListeners_.push_back(listener);
  return listener.id;
}

bool Actor::RemoveEventListener(uint32 listenerId) {
  for (auto it = eventListeners_.begin(); it != eventListeners_.end(); ++it) {
    if (it->id == listenerId) {
      eventListeners_.erase(it);
      return true;
    }
  }
  return false;
}

void Actor::ClearEventListeners() {
  eventListeners_.clear();
}

void Actor::executeUpdateListeners(UpdatePhase phase, float deltaTime) {
  if (updateListeners_.empty()) {
    return;
  }

  iteratingUpdateListeners_ = true;
  size_t listenerCount = updateListeners_.size();

  for (size_t i = 0; i < listenerCount; ++i) {
    auto& listener = updateListeners_[i];
    if (!listener.active || listener.phase != phase || !listener.callback) {
      continue;
    }
    listener.callback(*this, deltaTime);
  }

  iteratingUpdateListeners_ = false;
  if (updateListenersDirty_) {
    compactUpdateListeners();
  }
}

void Actor::compactUpdateListeners() {
  updateListeners_.erase(
      std::remove_if(updateListeners_.begin(), updateListeners_.end(),
                     [](const UpdateListener& listener) {
                       return !listener.active;
                     }),
      updateListeners_.end());
  updateListenersDirty_ = false;
}

bool Actor::OnEvent(const Event& event) {
  if (!eventReceiveEnabled_) {
    return false;
  }

  for (const auto& listener : eventListeners_) {
    if (listener.type == event.getType()) {
      if (listener.callback(event)) {
        return true;
      }
    }
  }

  return false;
}


bool Actor::DispatchEvent(const Event& event) {
  if (event.isPropagationStopped()) {
    return false;
  }

  if (OnEvent(event)) {
    return true;
  }

  for (auto it = children_.begin(); it != children_.end(); ++it) {
    if (*it) {
      if ((*it)->DispatchEvent(event)) {
        return true;
      }
    }
  }

  return false;
}

bool Actor::BubbleEvent(const Event& event) {
  if (!parent_) {
    return false;
  }

  if (parent_->OnEvent(event)) {
    return true;
  }

  return parent_->BubbleEvent(event);
}

}
