#pragma once

#include <frostbite2D/base/RefObject.h>
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <frostbite2D/utils/intrusive_list.hpp>
#include <string>
#include <vector>

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();

  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 Vec2& GetPosition() const { return position_; }
  void SetPosition(const Vec2& pos) { position_ = pos; }
  void SetPosition(float x, float y) { position_ = Vec2(x, y); }

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

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

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

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

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

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

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

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

  void UpdateChildren(float deltaTime);
  void RenderChildren();

private:
  Actor* parent_;
  Scene* scene_;
  ActorList children_;

  std::string name_;
  Vec2 position_;
  float rotation_;
  Vec2 scale_;
  Vec2 size_;
  bool visible_;
  int zOrder_;
  float opacity_;

  friend class Scene;
};

}

