#pragma once

#include <string>
#include <vector>
#include <memory>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtx/rotate_vector.hpp>

#include <yaml-cpp/yaml.h>
#include "../utils/UID.h"

class Component;

class Object
{
public:
    Object(const std::string &name);
    ~Object();

    const std::string &GetName() const;
    void SetName(const std::string &name);

    glm::vec2 GetLocalPosition() const;
    void SetLocalPosition(glm::vec2 pos);
    glm::vec2 GetWorldPosition() const;

    float GetLocalRotation() const;
    void SetLocalRotation(float deg);
    float GetWorldRotation() const;

    void SetParent(Object *parent);
    Object *GetParent() const;
    void AddChild(std::shared_ptr<Object> child);
    void RemoveChild(Object *child);
    std::vector<std::shared_ptr<Object>> &GetChildren();

    bool GetVisable() const;
    void SetVisable(bool state);

    template <typename T>
    std::shared_ptr<T> GetComponent() const;
    template <typename T>
    std::shared_ptr<T> AddComponent();
    template <typename T>
    void RemoveComponent();

    void Save(YAML::Emitter &out) const;
    void Load(const YAML::Node &node);

    UID uid;
    int layer = 0;

private:
    bool visable = true;
    std::string name;
    glm::vec2 localPosition{0.0f};
    float localRotationDeg = 0.0f; // Rotation in degrees
    Object *parent = nullptr;
    std::vector<std::shared_ptr<Object>> children;
    std::vector<std::shared_ptr<Component>> components;
};

template <typename T>
std::shared_ptr<T> Object::GetComponent() const
{
    for (const auto &comp : components)
        if (auto casted = std::dynamic_pointer_cast<T>(comp))
            return casted;
    return nullptr;
}

template <typename T>
std::shared_ptr<T> Object::AddComponent()
{
    auto existing = GetComponent<T>();
    if (existing)
        return existing;

    auto component = std::make_shared<T>(this);
    components.push_back(component);
    return component;
}

template <typename T>
void Object::RemoveComponent()
{
    components.erase(std::remove_if(components.begin(), components.end(),
                                    [](const std::shared_ptr<Component> &comp)
                                    {
                                        return std::dynamic_pointer_cast<T>(comp) != nullptr;
                                    }),
                     components.end());
}
