; SPDX-FileCopyrightText: 2023 Jummit
;
; SPDX-License-Identifier: GPL-3.0-or-later

;; An ECS world with systems and entities.
;; Provides a way to delete entities by their index while iterating.

(fn new-world [systems entities]
  (local world entities)
  (var to-remove {})
  (var err nil)
  (var err-timer nil)
  (var shown {})

  (fn world.remove [entity]
    "Marks the entity to be deleted after at the end of the world.run call."
    (tset to-remove entity true))

  (fn world.replace [entities]
    "Replaces all entities with the given ones."
    (table.move entities 1 (length entities) 1 world)
    (tset world (+ (length world) 1) nil))

  (fn clear-removed []
    (let [remove-order (icollect [id _ (pairs to-remove)] id)]
    (table.sort remove-order (fn [a b] (< b a)))
    (each [_ i (ipairs remove-order)]
      (table.remove world i))
    (set to-remove {})))

  (fn world.run []
    "Run all systems once."
    (each [_ system (ipairs systems)]
      (case (xpcall (fn [] (system world)) debug.traceback)
        (false msg)
        (when (not (. shown msg))
          (tset shown msg true)
          (_G.trace msg)
          (set err msg)
          (set err-timer 300))))
    (when err
      (set err-timer (- err-timer 1))
      (_G.rect 0 0 300 8 15)
      (_G.print (string.match err "[^\n]+") 1 1 2 false 1 true)
      (when (< err-timer 0)
        (set err nil)))
    (clear-removed))

  world)

{: new-world}