-- functions for working with entities inexplicably missing
-- from the game API

local fn = {}

-- WARNING: INEFFICIENT AS FUCK
fn.identify = function(objref) --> objectid
	for _, o in pairs(minetest.get_connected_players()) do
		if objref == o then return o:get_player_name(), 'player' end
	end
	for id, le in pairs(minetest.luaentities) do
		if le.object == objref then return id, 'entity' end
	end
end

fn.handle = sorcery.lib.class {
	__newindex = function(self,key,newval)
		local hnd if self.player
			then hnd = minetest.get_player_by_name(self._id)
			else hnd = minetest.luaentities[self._id]
		end
		if key == 'id' then
			if type(newval) == 'string' then
				local p = minetest.get_player_by_name(newval)
				if p then
					self._id = newval
					self.player = true
					return
				end
			end
			if minetest.luaentities[newval] then
				self._id = newval
				self.player = false
			else error('attempted to assign invalid ID to entity handle') end
		elseif key == 'obj' then
			local no, kind = fn.identify(newval)
			if no then
				self._id = no
				if kind == 'player'
					then self.player = true
					else self.player = false
				end
			else error('attempted to assign invalid ObjectRef to entity handle') end
		elseif key == 'stack' and self.kind == 'item' then
			hnd:set_item(newval)
		end
	end;
	__index = function(self,key)
		local hnd if self.player then
			hnd = minetest.get_player_by_name(self._id)
		else
			hnd = minetest.luaentities[self._id]
		end
		if key == 'online' then
			return hnd ~= nil
		elseif key == 'id' then
			if self.player then return nil
			else return self._id end
		elseif key == 'obj' then
			if self.player
				then return hnd
				else return hnd.object
			end
		elseif key == 'kind' then
			if     self.player                  then return 'player'
			elseif hnd.name == '__builtin:item' then return 'item'
			                                    else return 'object' end
		elseif key == 'name' then
			if self.player then return self._id
			elseif self.kind == 'item' then
				return ItemStack(hnd.itemstring):get_name()
			else return hnd.name end
		elseif key == 'stack' and self.kind == 'item' then
			return ItemStack(hnd.itemstring)
		elseif key == 'height' then
			if kind == 'item' then return 0.5
			elseif kind == 'player' then
				local eh = hnd.object:get_properties().eye_height
				return eh and (eh*1.2) or 1
			else
				local box = hnd.object:get_properties().collisionbox
				if box then
					local miny,maxy = box[2], box[5]
					return maxy-miny, miny
				else return 0 end
			end
		end
	end;
	construct = function(h)
		local kind, id
		if type(h) == 'string' and minetest.get_player_by_name(h) ~= nil then
			kind = 'player';
			id = h
		elseif minetest.luaentities[h] then
			kind = 'entity';
			id = h
		else id, kind = fn.identify(h) end

		if not id then
			error('attempted to construct object handle from invalid value')
		end

		return {
			player = kind == 'player';
			_id = id;
		}
	end;
}

return fn
