#ifndef ENTITY_ADMIN_H
#define ENTITY_ADMIN_H

#include <string>
#include <vector>

#include <QPoint>
#include <QSize>
#include <QVector2D>

#include "Tick.h"

#include "entt/entity/registry.hpp"

class EntityAdmin
{
public:
    EntityAdmin() = default;
    EntityAdmin(const EntityAdmin&) = delete;
    EntityAdmin& operator=(const EntityAdmin&) = delete;

    // -- Queries / iteration ------------------------------------------------

    template <typename... Ts, typename Func>
    void forEach(Func&& f);

    template <typename... Ts, typename Func>
    void forEach(Func&& f) const;

    template <typename... Ts>
    bool hasAll(entt::entity entity) const;

    template <typename T>
    T& get(entt::entity entity);

    template <typename T>
    const T& get(entt::entity entity) const;

    bool isValid(entt::entity entity) const;
    void destroy(entt::entity entity);
    void clear();

    // -- Public component attachment ----------------------------------------
    // Used by systems (e.g. ShipSystem) to attach optional components after
    // a factory method has created the base entity.

    template <typename T, typename... Args>
    void addComponent(entt::entity entity, Args&&... args);

    template <typename T>
    void removeComponent(entt::entity entity);

    // -- Factory methods ----------------------------------------------------

    entt::entity spawnShip(QVector2D position, float hp, float maxHp,
                           float maxSpeed_tpt, float mainAcceleration_tptt,
                           float maneuveringAcceleration_tptt, float maxAngularAcceleration_rptt,
                           float maxRotationSpeed_rpt, float sensorRange_tiles,
                           const std::string& schematicId, bool isEnemy);

    entt::entity spawnStation(QPoint anchor, QSize footprint,
                              const std::vector<QPoint>& bodyCells,
                              float hp, float maxHp, bool isEnemy);

    entt::entity spawnDebris(QVector2D position, int amount, Tick despawnAt);

    entt::entity spawnHqProxy(QVector2D position, float hp, float maxHp);

    // Creates a bare entity for module child entities (weapons, salvage, repair).
    // Caller is responsible for attaching all required components.
    entt::entity createModuleEntity();

private:
    entt::entity createEntity();

    entt::registry m_registry;
};

// ---------------------------------------------------------------------------
// Template implementations
// ---------------------------------------------------------------------------

template <typename... Ts, typename Func>
void EntityAdmin::forEach(Func&& f)
{
    // Avoid view.each() — MSVC 2017 ICEs on the extended_storage_iterator it instantiates.
    for (entt::entity entity : m_registry.view<Ts...>())
    {
        f(entity, m_registry.get<Ts>(entity)...);
    }
}

template <typename... Ts, typename Func>
void EntityAdmin::forEach(Func&& f) const
{
    entt::registry& reg = const_cast<entt::registry&>(m_registry);
    for (entt::entity entity : reg.view<Ts...>())
    {
        f(entity, reg.get<Ts>(entity)...);
    }
}

template <typename... Ts>
bool EntityAdmin::hasAll(entt::entity entity) const
{
    return m_registry.all_of<Ts...>(entity);
}

template <typename T>
T& EntityAdmin::get(entt::entity entity)
{
    return m_registry.get<T>(entity);
}

template <typename T>
const T& EntityAdmin::get(entt::entity entity) const
{
    return m_registry.get<T>(entity);
}

template <typename T, typename... Args>
void EntityAdmin::addComponent(entt::entity entity, Args&&... args)
{
    m_registry.emplace<T>(entity, std::forward<Args>(args)...);
}

template <typename T>
void EntityAdmin::removeComponent(entt::entity entity)
{
    m_registry.remove<T>(entity);
}

#endif // ENTITY_ADMIN_H
