#pragma once
#include "../engine/Shared.h"
#include "Global.h"
#include "Log.h"
#include "Puzzle.h"
#include "rendering/Rendering.h"
#include <bgfx/bgfx.h>
#include <cstdint>

#define ENTITY_HANDLE(X)                                                                                               \
    struct X                                                                                                           \
    {                                                                                                                  \
        uint16_t Idx = UINT16_MAX;                                                                                     \
    };

namespace Game
{
    struct EntityRenderData
    {
        Vec4 TestColor{1.0f, 1.0f, 1.0f, 1.0f};
        Vec4 BaseColor{0.0f, 0.0f, 0.0f, 1.0f};
        Transform Transform;
        EMaterial MaterialHandle = EMaterial::UNDEFINED;
        Generated::TextureHandle TextureHandle;
        Generated::ModelHandle ModelH;
        bool Visible = true;

        void Render(const Model* models, const Material* materials, const Texture* textures);
    };

    ENTITY_HANDLE(CubeHandle);
    struct Cube
    {
        int32_t TestX = -1;
        int32_t TestY = -1;
        EntityRenderData EData;

        void Setup();
        void Update();
    };

    ENTITY_HANDLE(TestEntityHandle);
    struct TestEntity
    {
        EntityRenderData EData;

        void Setup();
    };

    ENTITY_HANDLE(PuzzleTileEntityHandle);
    struct PuzzleTileEntity
    {
        EntityRenderData EData;
    };

    ENTITY_HANDLE(UIQuadEntityHandle);
    struct UIQuadEntity
    {
        EntityRenderData EData;
    };

    class IEntityManager
    {
      public:
        virtual bool Setup(void*& ptr, bool forceReset) = 0;
    };

    template <typename T, typename HandleT, uint32_t C> class EntityManager : public IEntityManager
    {
      public:
        uint16_t Count = 0;
        T* Data = nullptr;
        uint32_t EntitySize = 0;
        T InvalidObject{};
        bool IsEnabled = true;

      public:
        // Returns true if size changed
        bool Setup(void*& ptr, bool forceReset)
        {
            bool changed = false;
            if (EntitySize != sizeof(T) || forceReset)
            {
                Count = 0;
                changed = true;
            }
            EntitySize = sizeof(T);
            Data = reinterpret_cast<T*>(ptr);
            ptr = (uint8_t*)ptr + (C * EntitySize);
            return changed;
        }

        HandleT New()
        {
            if (Data == nullptr)
            {
                ERROR_ONCE("Accessed EntityManager before setup!");
                return {};
            }
            if (Count >= C)
            {
                ERROR_ONCE("Too many entities!");
                return {};
            }
            Data[Count] = {};
            HandleT H;
            H.Idx = Count;
            ++Count;
            return H;
        }

        T& Get(HandleT handle)
        {
            if (handle.Idx > Count)
            {
                ERROR_ONCE("OOB Access!");
                return InvalidObject;
            }
            return Data[handle.Idx];
        }

        void Render(const Model* models, const Material* materials, const Texture* textures)
        {
            if (!IsEnabled) return;
            for (uint16_t i = 0; i < Count; ++i)
            {
                Get({i}).EData.Render(models, materials, textures);
            }
        }
    };

    struct WorldPuzzle
    {
        static constexpr Vec2 WorldCardSize{10.0f, 10.0f};
        Generated::PuzzleData Data;
        Vec3 WorldPosition;
        PuzzleTileEntityHandle TileHandles[Puzzle::Config::MaxCardsInPuzzle];
        UIQuadEntityHandle UIPlacedCards[Puzzle::Config::MaxCardsInPuzzle];
        bool IsSetup = false;

        void Setup();
        void Update();
    };

    class Level
    {
      public:
        EntityManager<Cube, CubeHandle, 1024> Cubes;
        EntityManager<TestEntity, TestEntityHandle, 32> Tests;
        EntityManager<PuzzleTileEntity, PuzzleTileEntityHandle, 1024> PuzzleTiles;
        EntityManager<UIQuadEntity, UIQuadEntityHandle, 1024> UIQuads;

      public:
        Generated::StaticPuzzleData PuzzleData;
        WorldPuzzle Puzzles[1];

      public:
        void Setup(GameData& data);
        void Update();
        void Render(uint16_t ViewID, const Model* models, const Material* materials, const Texture* textures);
    };
} // namespace Game
