var world = {}

var entities = {}

world.add_entity = function(entity_proto, data) {
  var entity = meme(entity_proto, data)
  if (is_function(entity.init))
    entity.init()
  entities[entity] = true
  return entity
}

world.destroy_entity = function(entity) {
  if (is_function(entity.on_destroy))
    entity.on_destroy()
  delete entities[entity]
}

world.update = function(dt) {
  arrfor(array(entities), function(e) {
    if (is_function(e.update))
      e.update(dt)
  })
}

world.each = function(fn) {
  arrfor(array(entities), fn)
}

world.query = function(filter_fn) {
  return filter(array(entities), filter_fn)
}

world.find = function(predicate) {
  var keys = array(entities)
  var i = 0
  for (i = 0; i < length(keys); i++) {
    if (predicate(keys[i]))
      return keys[i]
  }
  return null
}

world.clear = function() {
  arrfor(array(entities), function(e) {
    if (is_function(e.on_destroy))
      e.on_destroy()
  })
  entities = {}
}

world.load_level = function(json_array) {
  var i = 0
  var entry = null
  var proto = null
  var entity = null
  for (i = 0; i < length(json_array); i++) {
    entry = json_array[i]
    proto = entry.script ? use(entry.script) : {}
    entity = world.add_entity(proto, entry)
  }
}

world.count = function() {
  return length(array(entities))
}

world.snapshot = function() {
  var result = []
  arrfor(array(entities), function(e) {
    var snap = {}
    arrfor(array(e), function(k) {
      if (!is_function(e[k]))
        snap[k] = e[k]
    })
    push(result, snap)
  })
  return result
}

return world
