#pragma once

#include <frostbite2D/base/RefObject.h>
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <frostbite2D/types/uuid.h>
#include <frostbite2D/utils/intrusive_list.hpp>
#include <frostbite2D/event/event.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 <string>
#include <vector>
#include <functional>
#include <unordered_map>

namespace frostbite2D {

class Actor;
class Scene;

using ActorList = IntrusiveList<RefPtr<Actor>>;

class Actor : public RefObject, protected IntrusiveListValue<RefPtr<Actor>> {
public:
  using IntrusiveListValue<RefPtr<Actor>>::GetNext;
  using IntrusiveListValue<RefPtr<Actor>>::GetPrev;

  Actor();
  virtual ~Actor();

  virtual void Update(float deltaTime);
  virtual void Render();
  virtual void OnUpdate(float deltaTime) {}
  virtual void OnAdded(Actor* parent) {}

  void AddChild(RefPtr<Actor> child);
  void RemoveChild(RefPtr<Actor> child);
  void RemoveAllChildren();

  Actor* GetParent() const { return parent_; }
  Scene* GetScene() const { return scene_; }

  const std::string& GetName() const { return name_; }
  void SetName(const std::string& name) { name_ = name; }

  const UUID& GetUUID() const { return uuid_; }
  std::string GetUUIDString() const { return uuid_.toString(); }

  /**
   * @brief 获取锚点在父坐标系中的位置
   */
  const Vec2& GetPosition() const { return position_; }
  /**
   * @brief 设置锚点在父坐标系中的位置
   */
  void SetPosition(const Vec2& pos);
  void SetPosition(float x, float y);

  float GetRotation() const { return rotation_; }
  void SetRotation(float rotation);

  const Vec2& GetScale() const { return scale_; }
  void SetScale(const Vec2& scale);
  void SetScale(float scale);

  const Vec2& GetSkew() const { return skew_; }
  void SetSkew(const Vec2& skew);
  void SetSkew(float skewX, float skewY);

  const Transform2D& GetLocalTransform() const;
  const Transform2D& GetWorldTransform() const;

  const Vec2& GetSize() const { return size_; }
  void SetSize(const Vec2& size);
  void SetSize(float width, float height);

  /**
   * @brief 获取归一化锚点，(0, 0) 为左上角，(0.5, 0.5) 为中心
   */
  const Vec2& GetAnchor() const { return anchor_; }
  /**
   * @brief 设置归一化锚点，不会自动补偿 position
   */
  void SetAnchor(const Vec2& anchor);
  void SetAnchor(float x, float y);

  /**
   * @brief 获取未旋转矩形左上角在父坐标系中的位置
   */
  Vec2 GetTopLeftPosition() const;
  /**
   * @brief 设置未旋转矩形左上角在父坐标系中的位置
   */
  void SetTopLeftPosition(const Vec2& pos);
  void SetTopLeftPosition(float x, float y);
  Vec2 LocalToWorld(const Vec2& point) const;
  Vec2 WorldToLocal(const Vec2& point) const;
  Rect GetWorldBounds() const;
  bool ContainsWorldPoint(const Vec2& point) const;
  bool ContainsWorldPoint(float x, float y) const;

  bool IsVisible() const { return visible_; }
  void SetVisible(bool visible) { visible_ = visible; }

  int GetZOrder() const { return zOrder_; }
  void SetZOrder(int zOrder);

  float GetOpacity() const { return opacity_; }
  void SetOpacity(float opacity);
  float GetWorldOpacity() const;

  ActorList& GetChildren() { return children_; }
  const ActorList& GetChildren() const { return children_; }

  bool IsEventReceiveEnabled() const { return eventReceiveEnabled_; }
  void EnableEventReceive() { eventReceiveEnabled_ = true; }
  void DisableEventReceive() { eventReceiveEnabled_ = false; }

  int32 GetEventPriority() const { return eventPriority_; }
  void SetEventPriority(int32 priority) { eventPriority_ = priority; }

  bool IsEventInterceptEnabled() const { return eventInterceptEnabled_; }
  void EnableEventIntercept() { eventInterceptEnabled_ = true; }
  void DisableEventIntercept() { eventInterceptEnabled_ = false; }

  enum class UpdatePhase {
    BeforeChildren,
    AfterChildren
  };

  using UpdateCallback = Function<void(Actor&, float)>;
  using EventCallback = std::function<bool(const Event&)>;

  uint32 AddUpdateListener(UpdateCallback callback,
                           UpdatePhase phase = UpdatePhase::BeforeChildren);
  bool RemoveUpdateListener(uint32 listenerId);
  void ClearUpdateListeners();

  uint32 AddEventListener(EventType type, EventCallback callback);
  bool RemoveEventListener(uint32 listenerId);
  void ClearEventListeners();

  virtual bool OnEvent(const Event& event);

  bool DispatchEvent(const Event& event);
  bool BubbleEvent(const Event& event);

 protected:
  void SetParent(Actor* parent) { parent_ = parent; }
  void SetScene(Scene* scene) { scene_ = scene; }

  void insertChildByZOrder(RefPtr<Actor> child);
  void UpdateChildren(float deltaTime);
  void RenderChildren();

  virtual void OnTransformChanged() {};

private:
  void markChildrenOrderDirty();
  void reorderChildrenByZOrder();

  Actor* parent_;
  Scene* scene_;
  ActorList children_;

  UUID uuid_;
  std::string name_;
  Vec2 position_;
  float rotation_;
  Vec2 scale_ = Vec2(1.0f, 1.0f);
  Vec2 skew_;
  Vec2 size_;
  Vec2 anchor_;
  bool visible_;
  int zOrder_;
  float opacity_;

  mutable Transform2D localTransform_;
  mutable Transform2D worldTransform_;
  mutable bool transformDirty_ = true;

  bool eventReceiveEnabled_ = false;
  int32 eventPriority_ = 0;
  bool eventInterceptEnabled_ = false;

  struct UpdateListener {
    uint32 id;
    UpdatePhase phase;
    UpdateCallback callback;
    bool active = true;
  };
  std::vector<UpdateListener> updateListeners_;
  uint32 nextUpdateListenerId_ = 1;
  bool iteratingUpdateListeners_ = false;
  bool updateListenersDirty_ = false;
  bool iteratingChildren_ = false;
  bool childrenOrderDirty_ = false;

  struct EventListener {
    uint32 id;
    EventType type;
    EventCallback callback;
  };
  std::vector<EventListener> eventListeners_;
  uint32 nextListenerId_ = 1;

  void markTransformDirty();
  void updateLocalTransform() const;
  void updateWorldTransform() const;
  void executeUpdateListeners(UpdatePhase phase, float deltaTime);
  void compactUpdateListeners();

  friend class Scene;
};

}

