using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using gaemstone.ECS.Utility; using static flecs_hub.flecs; namespace gaemstone.ECS.Internal; public unsafe class Iterator : IDisposable { public ecs_iter_t* Handle; private readonly bool _handleIsOwned; public World World => new(Handle->world); public int Count => Handle->count; public IteratorFlags Flags => (IteratorFlags)(uint)Handle->flags; public bool IsValid => (Flags & IteratorFlags.IsValid) != 0; public TimeSpan DeltaTime => TimeSpan.FromSeconds(Handle->delta_time); public TimeSpan DeltaSystemTime => TimeSpan.FromSeconds(Handle->delta_system_time); public Iterator(ecs_iter_t* handle) { Handle = handle; _handleIsOwned = false; } public Iterator(ecs_iter_t value) { Handle = GlobalHeapAllocator.Instance.AllocateCopy(value); _handleIsOwned = true; } public void Dispose() { if (Handle == null) return; // When an iterator is iterated until completion, // ecs_iter_fini will be called automatically. if (IsValid) ecs_iter_fini(Handle); if (_handleIsOwned) GlobalHeapAllocator.Instance.Free(Handle); Handle = null; } public Entity GetVar(Variable var) => new(ecs_iter_get_var(Handle, var.Index)); public Iterator SetVar(Variable var, Entity entity) { ecs_iter_set_var(Handle, var.Index, entity); return this; } public virtual bool Next() => ecs_iter_next(Handle); public bool Any() { using (this) return Next(); } public Entity First() => new(ecs_iter_first(Handle)); public IEnumerable GetAllEntities() { while (Next()) for (var i = 0; i < Count; i++) yield return Entity(i); } public bool FieldIsSet(int index) => ecs_field_is_set(Handle, index); public Id FieldId(int index) => new(ecs_field_id(Handle, index)); public bool FieldIs(int index, Id id) => ecs_field_id(Handle, index) == id.Value; public Entity Entity(int index) => new(Handle->entities[index]); public Span Field(int index) where T : unmanaged { var size = (ulong)Unsafe.SizeOf(); var isSelf = ecs_field_is_self(Handle, index); var pointer = ecs_field_w_size(Handle, size, index); return new(pointer, isSelf ? Count : 1); } public Span FieldOrEmpty(int index) where T : unmanaged => FieldIsSet(index) ? Field(index) : Span.Empty; public SpanToRef Field(int index, T _ = null!) where T : class => new(Field(index)); public SpanToRef FieldOrEmpty(int index, T _ = null!) where T : class => FieldIsSet(index) ? Field(index) : SpanToRef.Empty; public override string ToString() => ecs_iter_str(Handle).FlecsToStringAndFree()!; } public readonly ref struct SpanToRef where T : class { public static SpanToRef Empty => default; private readonly Span _span; public int Length => _span.Length; public T this[int index] => (T)_span[index].Target!; public T? GetOrNull(int index) => ((index >= 0) && (index < Length)) ? this[index] : null; internal SpanToRef(Span span) => _span = span; }