#pragma once

/*
	GameEngine and Editor
	by John Ryland
	Copyright (c) 2023
*/

////////////////////////////////////////////////////////////////////////////////////
//	Game State

#include "SchemaFile.h"
#include <cstdio>
#include <vector>
#include <deque>
#include <string>
#include <unordered_map>

namespace GameEngine {

// In the editor we want to have the appearance of editing component types, however
// in reality each edit creates a new component type and the existing component type is disabled

// Aliases of some common std types
template <typename T>
using Array            = std::vector<T>;

template <typename T>
using List             = std::deque<T>;

template <typename K, typename T>
using Map              = std::unordered_map<K,T>;

using String           = std::string;

using StringList       = Array<String>;

template <typename T>
using Dict             = Map<String,T>;

// Game state types
using EntityId         = uint64_t;
using ComponentTypeId  = uint64_t;

// Not intended for use during runtime
struct ComponentInstanceRef
{
    ComponentTypeId                       componentId;
    //EntityId                              entityId;
    void*                                 data;
};

const size_t MIN_COMPONENT_SIZE = sizeof(EntityId);
const size_t MAX_COMPONENT_SIZE = 1000;  // Overhead of 24 bytes for the pool so each ComponentPoolData is 1024 bytes
const size_t MAX_COMPONENTS_PER_POOL = MAX_COMPONENT_SIZE / MIN_COMPONENT_SIZE;

struct ComponentPoolData
{
    uint64_t                              occupied[(MAX_COMPONENTS_PER_POOL + 63) / 64];
    uint32_t                              count;
    uint32_t                              reserved;
    uint8_t                               data[MAX_COMPONENT_SIZE];
};

const size_t COMPONENT_POOL_SIZE = sizeof(ComponentPoolData);

struct ComponentPool
{
    List<ComponentPoolData>               data;
};

// Not intended for use during runtime
struct ComponentType
{
    ComponentTypeId                       id;
    String                                name;
    GameRuntime::StructDescription        fields;
    size_t                                size;
    Array<EntityId>                       entities;
    ComponentPool                         data;
    bool                                  enabled;
};

// Not intended for use during runtime
struct EntityRef
{
    EntityId                              id;
    //Array<ComponentInstanceRef>           components;
    Map<ComponentTypeId,void*>            components;
};

// Not intended for use during runtime
using ComponentTypes  = Map<ComponentTypeId,ComponentType>;
using Entities        = Map<EntityId,EntityRef>;

const EntityId               INVALID_ENTITY_ID         = UINT64_MAX;
const ComponentTypeId        INVALID_COMPONENT_TYPE_ID = UINT64_MAX;

class GameState
{
public:
    GameState(GameRuntime::Schema* schema);

    void RegisterComponentType();
    StringList AllComponentTypeNames();
    ComponentTypeId FindComponentTypeByName(String componentName);
    GameRuntime::Schema* Schema();
    void NewEntity();
    void NewComponent();
    void RemoveStaleComponentReferences();

private:
    EntityId               lastUsedEntityId = 0;
    ComponentTypeId        lastUsedComponentTypeId = 0;

    GameRuntime::Schema*   schema;
    Entities               entities;
    ComponentTypes         components;
};

} // GameEngine namespace
