// part of uber 3
constructor TWorld.Init(Eng: TEngine);
begin
{$IF SizeOf(TEntity) > TEntityPoolStaticInfo.UBER_ENTITY_MAX_SIZE}
    {$error TEntity is too large}
    System.Writeln('Error: TEntity is too large: SizeOf(TEntity)=', SizeOf(TEntity), ' > UBER_ENTITY_MAX_SIZE=', TEntityPoolStaticInfo.UBER_ENTITY_MAX_SIZE);
    Halt(1);
{$ENDIF}
  FConstructor := TConstructor(@TEntity.Init);
  FPool.Init;
  FEng := Eng;
  FLastIndex := 0;
  if FPool.Alloc(FRoot) then begin
    Initialize(FRoot);
    FConstructor(FRoot, TypeOf(TEntity));
    //FRoot^.Init;
    //FRoot^.VMT := TypeOf(TEntity);
  end;
  FKillQue := nil;
end;

procedure _DoneEntity(Entity: PEntity);
begin
  Entity^.Done;
  Finalize(Entity^);
end;

procedure KillEntity(Entity: PEntity);
var
  N, It: PEntity;
begin
  It := Entity^.FSubFirst;
  while It <> nil do begin
    N := It^.FNext;
    KillEntity(It);
    It := N;
  end;
  Entity^.Kill;
  Entity^.SetParent(nil);
  Entity^.FId := 0;
  // FIXME me FPool is not visible here!
  World.FPool.Dealloc(Entity);
end;

destructor TWorld.Done;
begin
  KillEntity(FRoot);
  FPool.DoForEachSemiInitialized(@_DoneEntity);
  FPool.Done;
end;

procedure TWorld.Render;
var
  It: PEntity;
begin
  It := FRoot^.FSubFirst;
  while It <> nil do begin
    It^.Render;
    It := It^.FNext;
  end;
end;

procedure TWorld.Update;
var
  It: PEntity;
  N: PEntity;
begin
  It := FRoot^.FSubFirst;
  while It <> nil do begin
    It^.Update;
    It := It^.FNext;
  end;
  while FKillQue <> nil do begin
    // FIXME Kill and dealloc recursively
    // FIXME Check if parent and subobject both ware killed
    N := FKillQue^.FNextFree;
    if FKillQue^.FId <> 0 then begin
      KillEntity(FKillQue);
    end;
    FKillQue := N;
  end;
end;

function TWorld.Spawn(Parent: PEntity): PEntity;
begin
  Result := Spawn(TypeOf(TEntity), Parent);
end;

function TWorld.Spawn(VMT: Pointer; Parent: PEntity = nil): PEntity;
begin
  if Parent = nil then
    Parent := FRoot;
  if FPool.Alloc(Result) {or (Result^.VMT <> VMT)} then begin
    Initialize(Result^);
    FConstructor(Result, VMT);
  end;
  Inc(FLastIndex);
  Result^.FId := FLastIndex;
  Result^.FEng := FEng;
  Result^.SetParent(Parent);
  Result^.OnSpawn;
  Result^.FNextFree := nil;
end;

function _IsInFreeQue(World: PWorld; Entity: PEntity): Boolean; inline;
var
  It: PEntity;
begin
  It := World^.FKillQue;
  while It <> nil do begin
    if It = Entity then
      Exit(True);
    It := It^.FNextFree;
  end;
  Exit(False);
end;

procedure TWorld.Kill(Entity: PEntity);
begin
  // Already in kill queue? Skip
  if _IsInFreeQue(@Self, Entity) then
    Exit;
  // Put to the kill queue list, actually kill at update end
  Entity^.FNextFree := FKillQue;
  FKillQue := Entity;
end;

procedure TWorld.LogMemoryStats;
var
  Statistics: TEntityPool.TStatistics;
begin
  FPool.ComputeStatistics(@Statistics);
  Log('TWorld: ' + IntToStr(Statistics.AllocedCount) + ' entities (' + IntToStr(Statistics.Pages) + ' pages, ' + IntToStr(Statistics.Size) + ' bytes, SizeOf(TEntity)=' + IntToStr(SizeOf(TEntity)) + ')');
end;
