#pragma once

#include <concepts>
#include <Core/Common/CoreUtils.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
#include <Engine/Class.h>
#include <Entity/EntityManager.h>
#include <type_traits>

// Add any new fields into the concept below
#define DECLARE_ENTITY()                                                                                               \
    Entity*                     Base;                                                                                  \
    static const Juliet::Class* Kind;

// Will register the class globally at launch
#define DEFINE_ENTITY(entity)                                                                                          \
    constexpr Juliet::Class entityKind##entity(#entity, sizeof(#entity) / sizeof(char));                               \
    const Juliet::Class*    entity::Kind = &entityKind##entity;

namespace Game
{
    using DerivedType = void*;

    struct Entity final
    {
        EntityID             ID;
        const Juliet::Class* Kind;
        DerivedType          Derived;
        float                X, Y;
    };

    template <typename EntityType>
    concept EntityConcept = requires(EntityType entity) {
        requires std::same_as<decltype(entity.Kind), const Juliet::Class*>;
        requires std::same_as<decltype(entity.Base), Entity*>;
    };

    template <typename EntityType>
        requires EntityConcept<EntityType>
    bool IsA(const Entity* entity)
    {
        return entity->Kind == EntityType::Kind;
    }

    template <typename EntityType>
        requires EntityConcept<EntityType>
    EntityType* MakeEntity(EntityManager& manager, float x, float y)
    {
        auto*       arena  = manager.Arena;
        EntityType* result = Juliet::ArenaPushStruct<EntityType>(arena);
        Entity      base;
        base.X       = x;
        base.Y       = y;
        base.Derived = result;
        base.Kind    = EntityType::Kind;
        manager.Entities.PushBack(base);

        result->Base = manager.Entities.Back();

        RegisterEntity(manager, &base);

        return result;
    }

    template <typename EntityType>
        requires EntityConcept<EntityType>
    EntityType* DownCast(Entity* entity)
    {
        Assert(IsA<EntityType>(entity));
        return static_cast<EntityType*>(entity->Derived);
    }

} // namespace Game
