Code/k/ECS/Core/World.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sandbox.k.ECS.Core;

public class World
{
	private readonly EntityManager _entityManager = new();
	private readonly ComponentStorageContainer _componentStorages = new();
	private readonly Dictionary<int, int> _entityComponentCounts = new();
	private static World _default;

	public ComponentStorageContainer ComponentStorages => _componentStorages;
	public EntityManager EntityManager => _entityManager;
	public static World Default => _default ??= new();
	
	public static void ResetDefault() => _default = new();
	
	// Entity management
	public int CreateEntity() => _entityManager.CreateEntity();
	
	public void DestroyEntity(int entity)
	{
		if (!_entityManager.IsAlive(entity)) return;

		foreach ( var (_, storage) in _componentStorages.Values )
		{
			storage.Remove( entity );
		}
		
		_entityComponentCounts.Remove(entity);
		_entityManager.DestroyEntity(entity);
	}
	
	// Component management
	public void AddComponent<T>(int entity, T component) where T : struct
	{
		var storage = GetOrCreateStorage<T>();
		if ( storage == null )
		{
			Log.Error( $"Could not create storage for component {typeof(T).Name}" );
			return;
		}
		storage.Add(entity, component);
		IncrementComponentCount(entity);
	}
	
	public void RemoveComponent<T>(int entity) where T : struct
	{
		if (HasComponent<T>(entity))
		{
			GetStorage<T>().Remove(entity);
			DecrementComponentCount(entity);
		}
	}
	
	private void IncrementComponentCount(int entity)
	{
		if (!_entityComponentCounts.ContainsKey(entity))
			_entityComponentCounts[entity] = 0;
		_entityComponentCounts[entity]++;
	}
	
	private void DecrementComponentCount(int entity)
	{
		if (!_entityComponentCounts.ContainsKey(entity))
			return;

		_entityComponentCounts[entity]--;
		if (_entityComponentCounts[entity] <= 0)
		{
			_entityComponentCounts.Remove(entity);
			DestroyEntity(entity);
		}
	}
	
	public bool HasComponent(Type type, int entity)
	{
		if (!_componentStorages.TryGetValue(type, out var storage))
			return false;
			
		return storage.Has(entity);
	}
	
	public bool HasComponent<T>(int entity) where T : struct
	{
		if (!_componentStorages.TryGetValue(typeof(T), out var storage))
			return false;
			
		return storage.Has(entity);
	}
	
	public T GetComponent<T>(int entity) where T : struct => GetStorage<T>().Get(entity);
	
	public ref T GetComponentRef<T>(int entity) where T : struct => ref GetStorage<T>().GetRef(entity);
	
	// Storage management
	private ComponentStorage<T> GetStorage<T>() where T : struct
	{
		if (!_componentStorages.TryGetValue(typeof(T), out var storage))
			throw new KeyNotFoundException($"Trying to get component {typeof(T).Name} that does not exist on the entity");
			
		return (ComponentStorage<T>)storage;
	}
	
	private ComponentStorage<T> GetOrCreateStorage<T>() where T : struct
	{
		if (!_componentStorages.TryGetValue(typeof(T), out var storage))
		{
			var instance = new ComponentStorage<T>();
			storage = instance;
			_componentStorages.AddStorage( instance );
		}
		
		return (ComponentStorage<T>)storage;
	}
	
	// Systems query shortcuts
	public IEnumerable<int> GetEntitiesWith<T>() where T : struct
	{
		if (!_componentStorages.TryGetValue(typeof(T), out var storage))
			return Enumerable.Empty<int>();
			
		return ((ComponentStorage<T>)storage).GetAllEntities();
	}
	
	public IEnumerable<int> GetEntitiesWithout<T>() where T : struct
	{
		var entities = new HashSet<int>();
		foreach ( var (key, storage) in _componentStorages.Values )
		{
			if ( key == typeof(T) ) continue;
			foreach ( var entity in storage.GetAllEntities() )
			{
				entities.Add( entity );
			}
		}
		
		return entities;
	}
	
	public IEnumerable<int> GetEntitiesWith<T1, T2>() where T1 : struct where T2 : struct => GetEntitiesWith<T1>().Intersect(GetEntitiesWith<T2>());
	
	public IEnumerable<int> GetFilter(IEnumerable<int> withFilter, IEnumerable<int> withoutFilter) => withFilter.Except(withoutFilter);
}