;; entity definitions.
(define entity-defs (make-vector etype:_MAX_VALUE_))

;; constructed entities.
;; each entity is a singleton.
;; this is because the entity doesn't have any internal state.
;; all player objects share the same animation state.
(define entity-objects (make-vector etype:_MAX_VALUE_))

define otype-prop?(otype name)
  edef:prop? entity-defs[otype] name

define otype-prop-ref(otype name)
  edef:get-prop entity-defs[otype] name


define otype-name(otype)
  if between?(otype 0 etype:_MAX_VALUE_)
    enum:find-value(etype otype)
    number->string(otype)

define edef-name(def)
  otype-name edef:index(def)


(define bdcff-entity-map #nil)
(define bdcff-legend-map #nil)

;; BDCFF entity name -> etype index.
;; negative index means "scanned version".
define build-bdcff-entity-map()
  define map dict:new()
  ;;
  define add-name(nn idx scn alt)
    if {string? nn}
      dict:put! map nn idx
    if {string? alt}
      dict:put! map alt idx
    if {string? scn}
      dict:put! map scn {- idx}
      if {{string? nn} and {idx <> 0}}
        dict:put! map string-append("SCANN_" nn) {- idx}
  ;;
  define nn
  define idx 0
  while {idx <> vector-length(entity-defs)}
    add-name (edef:bdcff-name {entity-defs[idx]}) idx
      (lax-cdr (edef:get-prop {entity-defs[idx]} 'P_SCANNED_NAME))
      (lax-cdr (edef:get-prop {entity-defs[idx]} 'P_ALT_NAME))
    inc! idx
  ;; hacks for some old BDCFF
  ;; should "...S" be "scanned"?
  ;add-name "EXPLOSION1S" etype:O_EXPLODE_1 #nil
  ;add-name "EXPLOSION2S" etype:O_EXPLODE_2 #nil
  ;add-name "EXPLOSION3S" etype:O_EXPLODE_3 #nil
  ;add-name "EXPLOSION4S" etype:O_EXPLODE_4 #nil
  ;add-name "EXPLOSION5S" etype:O_EXPLODE_5 #nil
  ;add-name "EXPLOSION1D" etype:O_PRE_DIA_1 #nil
  ;add-name "EXPLOSION2D" etype:O_PRE_DIA_2 #nil
  ;add-name "EXPLOSION3D" etype:O_PRE_DIA_3 #nil
  ;add-name "EXPLOSION4D" etype:O_PRE_DIA_4 #nil
  ;add-name "EXPLOSION5D" etype:O_PRE_DIA_5 #nil
  gset! bdcff-entity-map map


;; BDCFF entity char -> etype index.
define build-bdcff-legend-map()
  define map dict:new()
  define nn
  define idx 0
  while {idx <> vector-length(entity-defs)}
    {nn := (edef:bdcff-char {entity-defs[idx]})}
    if {{fixnum? nn} and between?(nn 32 126)}
      dict:put! map number->string(nn) idx
    inc! idx
  gset! bdcff-legend-map map


define setup-entity-objects(def-eobj)
  define def-count 0
  define uniq-count 0
  define idx 0
  define handlers #nil
  define hh
  while {idx <> vector-length(entity-defs)}
    assert {edef:entity? entity-defs[idx]} string-append("undefined entity #" otype-name(idx))
    if {null? edef:object(entity-defs[idx])}
      then
        inc! def-count
        {entity-objects[idx] := def-eobj}
      else
        {hh := edef:object(entity-defs[idx])}
        if {any-false? memq(hh handlers)}
          then
            inc! uniq-count
            {handlers := cons(hh handlers)}
        {entity-objects[idx] := hh}
    inc! idx
  printf "%o known objects (%o unique handlers).\n" vector-length(entity-defs) uniq-count
  if {positive? def-count}
    printf "%o objects are not implemented yet.\n" def-count


define register-entity(ent)
  edef:?entity ent
  define idx (edef:index ent)
  assert {fixnum? idx} "invalid entity index"
  assert {null? {entity-defs[idx]}}
    string-append("duplicate entity: idx=" (number->string idx) "; name=" (edef:name ent))
  ;if {null? (edef:constructor ent)} edef:constructor-set!(ent default-object-ctor)
  {entity-defs[idx] := ent}
  ent


define check-entities()
  define idx 0
  while {idx <> vector-length(entity-defs)}
    if {not {edef:entity? entity-defs[idx]}}
      then
        printf "prev: %o\n" enum:find-value(etype idx)
        error string-append("entity #" number->string(idx) " not defined!")
    inc! idx


;define dump-entities()
;  define idx 0
;  while {idx <> vector-length(entity-defs)}
;    printf "(register-entity "
;    edef:dump entity-defs[idx]
;    printf ")\n"
;    inc! idx
;    ;{idx := vector-length(entity-defs)}


define verbose-gc()
  define start ticks-msec()
  ::SYSTEM:collect-garbage()
  define time {ticks-msec() - start}
  if {time > 1}
    then
      printf "GC time: %d msecs\n" time
      ::SYSTEM:print-stats()
    else
      ::SYSTEM:print-stats()
