#pragma once
#include "component.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include <algorithm>
#include <cassert>

#include "camera_class.h"

namespace ecs {
    /**
     * Type alias for a unique identifier for an entity.
     */
    using EntityID = uint32_t;

class Entity : public std::enable_shared_from_this<Entity> {
public:
    /**
     * Builder for an Entity with a unique ID.
     */
    explicit Entity(EntityID id) : m_ID(id) {}
    ~Entity() = default;

    /**
     * No copy constructor or assignment operator to prevent copying.
     */
    Entity(const Entity&) = delete;
    Entity& operator=(const Entity&) = delete;

    /**
     * Move constructor and assignment operator to allow moving entities.
     */
    Entity(Entity&&) = default;
    Entity& operator=(Entity&&) = default;

    /**
     * Get the unique identifier for the entity.
     * @return The unique ID of the entity.
     */
    EntityID GetID() const { return m_ID; }

    /**
     * Add a component of type T to the entity.
     * If the component already exists, it returns the existing component.
     * @tparam T 
     * @param args Arguments to construct the component.
     * @return A shared pointer to the added or existing component.
     */
    template<typename T, typename... Args>
    std::shared_ptr<T> AddComponent(Args&&... args) {
        static_assert(std::is_base_of<Component, T>::value, "T must derive from Component");
        
        ComponentTypeID typeID = GetComponentTypeID<T>();
        
        // Vérifier si le composant existe déjà
        if (m_Components.find(typeID) != m_Components.end()) {
            return std::static_pointer_cast<T>(m_Components[typeID]);
        }
        
        // Créer et ajouter le composant
        auto component = std::make_shared<T>(std::forward<Args>(args)...);
        m_Components[typeID] = component;

        component->SetParent(shared_from_this());
        
        
        // Initialiser le composant
        component->Initialize();
        
        return component;
    }

    /**
     * Get a component of type T from the entity.
     * @tparam T 
     * @return A shared pointer to the component if it exists, nullptr otherwise.
     */
    template<typename T>
    std::shared_ptr<T> GetComponent() {
        static_assert(std::is_base_of<Component, T>::value, "T must derive from Component");
        
        ComponentTypeID typeID = GetComponentTypeID<T>();
        
        auto it = m_Components.find(typeID);
        if (it != m_Components.end()) {
            return std::static_pointer_cast<T>(it->second);
        }
        
        return nullptr;
    }

    /**
     * Check if the entity has a component of type T.
     * @tparam T 
     * @return true if the entity has the component, false otherwise.
     */
    template<typename T>
    bool HasComponent() const {
        static_assert(std::is_base_of<Component, T>::value, "T must derive from Component");
        
        ComponentTypeID typeID = GetComponentTypeID<T>();
        return m_Components.find(typeID) != m_Components.end();
    }

    /**
     * Remove a component of type T from the entity.
     * @tparam T 
     */
    template<typename T>
    void RemoveComponent() {
        static_assert(std::is_base_of<Component, T>::value, "T must derive from Component");
        
        ComponentTypeID typeID = GetComponentTypeID<T>();
        auto it = m_Components.find(typeID);
        
        if (it != m_Components.end()) {
            m_Components.erase(it);
        }
    }

    /**
     * Update all components of the entity.
     * @param deltaTime 
     */
    void UpdateComponents(float deltaTime) {
        for (auto& [typeID, component] : m_Components) {
            component->Update(deltaTime);
        }
    }

    /**
     * Get all components of the entity.
     * @return A constant reference to the map of components.
     */
    const std::unordered_map<ComponentTypeID, ComponentPtr>& GetAllComponents() const {
        return m_Components;
    }

    void SetCamera(camera_class* camera) {m_camera = camera; }
    camera_class* GetCamera() const { return m_camera; }

private:

    /**
     * Unique identifier for the entity.
     */
    EntityID m_ID;
    /**
     * Map to hold components associated with the entity.
     * The key is the type ID of the component, and the value is a shared pointer to the component.
     */
    std::unordered_map<ComponentTypeID, ComponentPtr> m_Components;

    // camera
    camera_class* m_camera = nullptr;
};

}  // namespace ecs
