using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using gaemstone.Utility; using static flecs_hub.flecs; namespace gaemstone.ECS; public unsafe class Iterator : IEnumerable , IDisposable { public Universe Universe { get; } public IteratorType? Type { get; } public ecs_iter_t Value; public bool Completed { get; private set; } public int Count => Value.count; public TimeSpan DeltaTime => TimeSpan.FromSeconds(Value.delta_time); public TimeSpan DeltaSystemTime => TimeSpan.FromSeconds(Value.delta_system_time); public Iterator(Universe universe, IteratorType? type, ecs_iter_t value) { Universe = universe; Type = type; Value = value; } public static Iterator FromTerm(Universe universe, Term term) { using var alloc = TempAllocator.Use(); var flecsTerm = term.ToFlecs(alloc); var flecsIter = ecs_term_iter(universe, &flecsTerm); return new(universe, IteratorType.Term, flecsIter); } public void Dispose() { GC.SuppressFinalize(this); // NOTE: When an iterator is iterated until completion, resources are automatically freed. if (!Completed) fixed (ecs_iter_t* ptr = &Value) ecs_iter_fini(ptr); } public void SetThis(Entity entity) { fixed (ecs_iter_t* ptr = &Value) ecs_iter_set_var(ptr, 0, entity); } public bool Next() { fixed (ecs_iter_t* ptr = &Value) { var result = Type switch { IteratorType.Term => ecs_term_next(ptr), IteratorType.Filter => ecs_filter_next(ptr), IteratorType.Query => ecs_query_next(ptr), IteratorType.Rule => ecs_rule_next(ptr), _ => ecs_iter_next(ptr), }; Completed = !result; return result; } } public EntityRef Entity(int index) => new(Universe, new(Value.entities[index])); public Span Field(int index) where T : unmanaged { fixed (ecs_iter_t* ptr = &Value) { var size = (ulong)Unsafe.SizeOf(); var isSelf = ecs_field_is_self(ptr, index); var pointer = ecs_field_w_size(ptr, size, index); return new(pointer, isSelf ? Count : 1); } } public Span MaybeField(int index) where T : unmanaged => FieldIsSet(index) ? Field(index) : default; public SpanToRef FieldRef(int index) where T : class => new(Field(index)); public bool FieldIsSet(int index) { fixed (ecs_iter_t* ptr = &Value) return ecs_field_is_set(ptr, index); } public bool FieldIs(int index) { fixed (ecs_iter_t* ptr = &Value) { var id = ecs_field_id(ptr, index); var comp = Universe.LookupOrThrow(); return id == comp.Entity.Value.Data; } } public override string ToString() { fixed (ecs_iter_t* ptr = &Value) return ecs_iter_str(ptr).FlecsToStringAndFree()!; } // IEnumerable implementation public IEnumerator GetEnumerator() { while (Next()) yield return this; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public enum IteratorType { Term, Filter, Query, Rule, }