Can you change the ECS AddComponents function so that it adds the components all at once instead of one by one? #pragma once

#include <cstdint>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <bitset>
#include <memory>
#include <typeinfo>

#include "Archetype.h"
#include "EventBus.h"
#include "System.h"
#include "ComponentMask.h"
#include "ComponentManager.h"


using ArchetypeId = std::vector<int>;
using EntityId = uint32_t;

class SystemBase;
class ECS
{
private:

	using SystemsArrayMap = std::unordered_map<std::uint8_t, std::vector<SystemBase*>>;

	using ComponentTypeIdBaseMap = std::unordered_map<size_t, ComponentBase*>;

	struct Record
	{
		Archetype* archetype;
		size_t index;
	};

	using EntityArchetypeMap = std::unordered_map<size_t, Record>;

	using ArchetypesArray = std::vector<Archetype*>;

	using ComponentMasks = std::unordered_map<size_t, ComponentMask>;


public:
	explicit ECS();

	~ECS();

	void Update(const std::uint8_t layer, const std::uint32_t delta);

	void RegisterEntity(const EntityId entityId);

	void DestroyEntity(const EntityId entityId);

	template <class C>
	void RegisterComponent();

	template <class C>
	const bool IsComponentRegistered() const;

	template<class C, typename... Args>
	C* AddComponent(const EntityId entityId, Args&&... args);

	template<class C>
	void RemoveComponent(const EntityId entityId);

	template<class C>
	C* GetComponent(const EntityId entityId);

	const bool HasComponent(const EntityId entityId, size_t compTypeId) const;

	template<class C>
	size_t GetComponentTypeId();

	template<class C>
	const bool HasComponent(const EntityId entityId);

	void RegisterSystem(const std::uint8_t& layer, SystemBase* system);

	void UnregisterSystem(const std::uint8_t& layer, SystemBase* system);

	template<class... Cs>
	std::vector<size_t> GetEntitiesWithComponents();

	Archetype* GetArchetype(const ComponentMask& componentMask);

	EntityId GetNewId();

	std::shared_ptr<EventBus> GetEventBus()
	{
		return eventBus_;
	}

	template <typename... Components, typename... Args>
	void AddComponents(const EntityId entityId, Args&&... args);

	template <std::size_t I, typename T, typename... Ts, typename... Args>
	std::enable_if_t<I != sizeof...(Ts), void> AddComponentsHelper(const EntityId entityId, Args&&... args);

	template <std::size_t I, typename T, typename... Ts, typename... Args>
	std::enable_if_t<I == sizeof...(Ts), void> AddComponentsHelper(const EntityId entityId, Args&&... args);

	//ComponentBase* CreateComponentByName(const std::string& componentName) {
	//	return componentRegistry_.CreateComponentByName(componentName);
	//}

	//std::shared_ptr<EventQueueManager> GetEventQueueManager()
	//{
	//	return eventQueueManager_;
	//}
private:
	std::shared_ptr<EventBus> eventBus_;

	//ComponentRegistry componentRegistry_;

	//std::shared_ptr<EventQueueManager> eventQueueManager_;

	EntityId entityIdCounter;

	SystemsArrayMap systems;

	ComponentTypeIdBaseMap componentMap;

	EntityArchetypeMap entityArchetypeMap;

	ArchetypesArray archetypes;

	ComponentMasks componentMasks;

	ComponentManager componentManager_;

	template<class C, typename... Args>
	C* TransferComponentsAndAdd(const EntityId entityId, Archetype* newArchetype, Archetype* oldArchetype, Args&... args);

	template<class C>
	void TransferComponentsAndRemove(size_t entityIndex, Archetype* oldArchetype, Archetype* newArchetype);

	size_t GetEntityIndex(const Archetype* archetype, EntityId entityId);

	void UpdateRecord(EntityId entityId, Archetype* archetype, Record& record);

	void UpdateEntitiesInArchetypes(Archetype* oldArchetype, Archetype* newArchetype, EntityId entityId);

};

template <typename... Components, typename... Args>
void ECS::AddComponents(const EntityId entityId, Args&&... args) {
	AddComponentsHelper<0, Components...>(entityId, std::forward<Args>(args)...);
}

template <std::size_t I, typename T, typename... Ts, typename... Args>
std::enable_if_t<I != sizeof...(Ts), void> ECS::AddComponentsHelper(const EntityId entityId, Args&&... args) {
	AddComponent<T>(entityId, std::get<I>(std::forward_as_tuple(args...)));
	AddComponentsHelper<I + 1, Ts...>(entityId, std::forward<Args>(args)...);
}

template <std::size_t I, typename T, typename... Ts, typename... Args>
std::enable_if_t<I == sizeof...(Ts), void> ECS::AddComponentsHelper(const EntityId entityId, Args&&... args) {
	AddComponent<T>(entityId, std::get<I>(std::forward_as_tuple(args...)));
}

template<class C>
void ECS::RegisterComponent()
{
	size_t componentTypeId = Component<C>::GetTypeID();

	if (componentMap.contains(componentTypeId))
		return; // can't re-register a type

	componentMap.emplace(componentTypeId, new Component<C>);
};

template<class C>
const bool ECS::IsComponentRegistered() const {
	size_t componentTypeId = Component<C>::GetTypeID();
	return (componentMap.contains(componentTypeId));
}

template<class C, typename... Args>
C* ECS::AddComponent(const EntityId entityId, Args&&... args) {
	// 1. Get the component type ID and check if the component already exists for the entity
	auto newComponentID = GetTypeID<C>();

	if (HasComponent(entityId, newComponentID)) { return nullptr; }

	// 2. Update the component mask for the entity and get the new archetype
	componentMasks[entityId].AddComponent(newComponentID);
	Archetype* newArchetype = GetArchetype(componentMasks[entityId]);

	// 3. Get and store the record of the entity
	Record& record = entityArchetypeMap[entityId];
	Archetype* oldArchetype = record.archetype;

	C* newComponent = nullptr;

	// 4. If the entity has existing components, transfer or add the new component
	if (oldArchetype) {
		newComponent = TransferComponentsAndAdd<C>(entityId, newArchetype, oldArchetype, args...);
	}
	// 5. If the entity has no existing components, allocate and add the new component
	else {
		size_t currentSize = componentManager_.FindCurrentSize(newComponentID, newArchetype, 0);
		newComponent = new (&newArchetype->componentData[0][currentSize]) C(std::forward<Args>(args)...);
	}

	// 6. Update the record and return the new component
	UpdateRecord(entityId, newArchetype, record);
	return newComponent;
}

template<class C>
void ECS::RemoveComponent(const EntityId entityId) {
	auto componentID = GetTypeID<C>();

	if (!IsComponentRegistered<C>() || !HasComponent(entityId, componentID)) {
		return;
	}

	componentMasks[entityId].RemoveComponent(componentID);

	Record& record = entityArchetypeMap[entityId];
	Archetype* oldArchetype = record.archetype;

	if (!oldArchetype) { return; }

	Archetype* newArchetype = GetArchetype(componentMasks[entityId]);
	size_t entityIndex = GetEntityIndex(oldArchetype, entityId);

	TransferComponentsAndRemove<C>(entityIndex, oldArchetype, newArchetype);

	UpdateRecord(entityId, newArchetype, record);
}

template<class C>
C* ECS::GetComponent(const EntityId entityId) {
	size_t componentTypeId = Component<C>::GetTypeID();

	if (!HasComponent(entityId, componentTypeId)) {
		return nullptr; // Component doesn't exist for the entity
	}

	const Archetype* archetype = entityArchetypeMap[entityId].archetype;
	if (!archetype) {
		return nullptr; // Archetype doesn't exist for the entity
	}

	size_t componentIndex = archetype->componentMask.ReturnArchetypeIndex(componentTypeId);

	auto entityIterator = std::find(
		archetype->entityIds.begin(),
		archetype->entityIds.end(),
		entityId
	);

	if (entityIterator == archetype->entityIds.end()) {
		return nullptr;
	}

	size_t entityIndex = entityIterator - archetype->entityIds.begin();
	size_t componentSize = componentMap[componentTypeId]->GetSize();

	C* component = reinterpret_cast<C*>(&archetype->componentData[componentIndex][entityIndex * componentSize]);
	return component;
}

template<class C>
size_t ECS::GetComponentTypeId() {
	auto newComponentId = Component<C>::GetTypeID();
	return newComponentId;
}

template<class C>
const bool ECS::HasComponent(const EntityId entityId) {
	return (componentMasks[entityId]).HasComponent(Component<C>::GetTypeID());
}

template<class... Cs>
std::vector<size_t> ECS::GetEntitiesWithComponents()
{
	std::vector<size_t> entities;

	ComponentMask mask;
	((mask.AddComponent(Component<Cs>::GetTypeID())), ...);

	for (const auto& [entityId, record] : entityArchetypeMap)
	{
		const Archetype* archetype = record.archetype;
		if (!archetype) {
			continue;
		}

		const ComponentMask& entityMask = componentMasks[entityId];

		if (mask.IsSubsetOf(entityMask)) {
			entities.push_back(entityId);
		}
	}
	return entities;
}

template<class C, typename... Args>
C* ECS::TransferComponentsAndAdd(const EntityId entityId, Archetype* newArchetype, Archetype* oldArchetype, Args&... args) {
	auto newComponentMask = newArchetype->componentMask;
	auto oldComponentMask = oldArchetype->componentMask;
	C* newComponent = nullptr;

	size_t entityIndex = GetEntityIndex(oldArchetype, entityId);

	for (size_t i = 0, fromIndex = 0, toIndex = 0; i < newComponentMask.Size() - 1; ++i) {
		if (newComponentMask.HasComponent(i)) {
			size_t currentSize = componentManager_.FindCurrentSize(i, newArchetype, toIndex);

			if (oldComponentMask.HasComponent(i)) {
				componentManager_.TransferComponentData(i, oldArchetype, newArchetype, fromIndex, toIndex, entityIndex);
				++fromIndex;
			}
			else {
				newComponent = new (&newArchetype->componentData[toIndex][currentSize]) C(args...);
			}
			toIndex++;
		}
	}
	UpdateEntitiesInArchetypes(oldArchetype, newArchetype, entityId);

	return newComponent;
}

template<class C>
void ECS::TransferComponentsAndRemove(size_t entityIndex, Archetype* oldArchetype, Archetype* newArchetype) {
	auto oldComponentMask = oldArchetype->componentMask;
	auto newComponentMask = newArchetype->componentMask;

	for (size_t i = 0, fromIndex = 0, toIndex = 0; i < oldComponentMask.Size(); ++i) {
		if (oldComponentMask.HasComponent(i)) {
			if (newComponentMask.HasComponent(i)) {
				componentManager_.RemoveAndAllocateComponentData(i, oldArchetype, newArchetype, fromIndex, toIndex, entityIndex);

				toIndex++;
			}
			else {
				componentManager_.SwapComponentData(i, oldArchetype, toIndex, entityIndex);
			}

			fromIndex++;
		}
	}

	std::iter_swap(oldArchetype->entityIds.begin() + entityIndex, oldArchetype->entityIds.end() - 1);
	oldArchetype->entityIds.pop_back();
}
#include "ComponentManager.h"

void ComponentManager::TransferComponentData(size_t compTypeId, Archetype* fromArch, Archetype* toArch, size_t fromIndex, size_t toIndex, size_t entityIndex) {
    MoveComponentData(compTypeId, fromArch, toArch, fromIndex, toIndex, entityIndex, toArch->entityIds.size());
}

void ComponentManager::SwapComponentData(size_t compTypeId, Archetype* arch, size_t toIndex, size_t entityIndex) {
    const auto lastEntity = arch->entityIds.size() - 1;

    MoveComponentData(compTypeId, arch, arch, toIndex, toIndex, lastEntity, entityIndex);
}

void ComponentManager::RemoveAndAllocateComponentData(size_t compTypeId, Archetype* fromArchetype, Archetype* toArchetype, size_t fromIndex, size_t toIndex, size_t entityIndex) {
    const ComponentBase* componentBase = GetComponentBase(compTypeId);
    const std::size_t componentDataSize = componentBase->GetSize();

    size_t currentSize = FindCurrentSize(compTypeId, toArchetype, toIndex);

    const size_t lastEntity = fromArchetype->entityIds.size() - 1;

    componentBase->MoveData(&fromArchetype->componentData[fromIndex][entityIndex * componentDataSize],
        &toArchetype->componentData[toIndex][currentSize]);

    if (entityIndex != lastEntity) {
        componentBase->MoveData(&fromArchetype->componentData[fromIndex][lastEntity * componentDataSize],
            &fromArchetype->componentData[fromIndex][entityIndex * componentDataSize]);
    }

    componentBase->DestroyData(&fromArchetype->componentData[fromIndex][lastEntity * componentDataSize]);
}

size_t ComponentManager::FindCurrentSize(size_t compTypeId, Archetype* archetype, size_t index) {
    const ComponentBase* compbase = GetComponentBase(compTypeId);
    const std::size_t& newCompDataSize = compbase->GetSize();

    std::size_t currentSize = archetype->entityIds.size() * newCompDataSize;
    std::size_t newSize = currentSize + newCompDataSize;

    if (newSize > archetype->componentDataSize[index]) {
        Reallocate(compTypeId, newCompDataSize, archetype, index);
    }

    return currentSize;
}

void ComponentManager::Reallocate(size_t compTypeId, const size_t newComponentDataSize, Archetype* archetype, size_t index) {
    const ComponentBase* componentBase = GetComponentBase(compTypeId);
    const std::size_t componentDataSize = componentBase->GetSize();

    archetype->componentDataSize[index] *= 2;
    archetype->componentDataSize[index] += newComponentDataSize;

    unsigned char* newData = new unsigned char[archetype->componentDataSize[index]];

    for (auto e = 0; e < archetype->entityIds.size(); ++e) {
        componentBase->MoveData(&archetype->componentData[index][e * newComponentDataSize],
            &newData[e * newComponentDataSize]);
        componentBase->DestroyData(&archetype->componentData[index][e * newComponentDataSize]);
    }

    delete[] archetype->componentData[index];
    archetype->componentData[index] = newData;
}

void ComponentManager::MoveComponentData(size_t compTypeId, Archetype* fromArch, Archetype* toArch, size_t fromIndex, size_t toIndex, size_t fromEntityIndex, size_t toEntityIndex) {
    const ComponentBase* componentBase = GetComponentBase(compTypeId);
    const std::size_t componentDataSize = componentBase->GetSize();

    componentBase->MoveData(&fromArch->componentData[fromIndex][fromEntityIndex * componentDataSize],
        &toArch->componentData[toIndex][toEntityIndex * componentDataSize]);

    componentBase->DestroyData(&fromArch->componentData[fromIndex][fromEntityIndex * componentDataSize]);
}
