function areas:player_exists(name)
	return minetest.auth_table[name] ~= nil
end

-- Save the areas table to a file
function areas:save()
	file, err = io.open(self.filename, "w")
	if err then
		return err
	end
	file:write(minetest.serialize(self.areas))
	file:close()
end

-- Load the areas table from the save file
function areas:load()
	file, err = io.open(self.filename, "r")
	if err then
		self.areas = self.areas or {}
		return err
	end
	self.areas = minetest.deserialize(file:read("*a"))
	if type(self.areas) ~= "table" then self.areas = {} end
	file:close()
end

-- Shorter than the table function
function areas:add(owner, name, pos1, pos2, parent)
	table.insert(areas.areas, {id=table.maxn(self.areas)+1, name=name,
			pos1=pos1, pos2=pos2, owner=owner, parent=parent})
end

-- Remove a area, and optionally it's children recursively.
-- If a area is deleted non-recursively the children will
-- have the removed area's parent as their new parent.
function areas:remove(id, recurse)
	if recurse then
		-- Recursively find child entries and remove them
		local cids = self:getChildren(id)
		for _, cid in pairs(cids) do
			self:removeArea(cid, true)
		end
	else
		-- Update parents
		local parent = self:getAreaById(id).parent
		local children = self:getChildren(id)
		for _, child in pairs(children) do
			-- The subarea parent will be niled out if the
			-- removed area does not have a parent
			areas.areas[self:getIndexById(child)].parent = parent

		end
	end

	-- Remove main entry
	table.remove(self.areas, self:getIndexById(id))
end

-- Checks if a area between two points is entirely contained by another area
function areas:isSubarea(pos1, pos2, id)
	local area = areas:getAreaById(id)
	if not area then
		return false
	end
	p1, p2 = area.pos1, area.pos2
	if (pos1.x >= p1.x and pos1.x <= p2.x) and
	   (pos2.x >= p1.x and pos2.x <= p2.x) and
	   (pos1.y >= p1.y and pos1.y <= p2.y) and
	   (pos2.y >= p1.y and pos2.y <= p2.y) and
	   (pos1.z >= p1.z and pos1.z <= p2.z) and
	   (pos2.z >= p1.z and pos2.z <= p2.z) then
		return true
	end
end

-- Returns a table (list) of children of an area given it's identifier
function areas:getChildren(id)
	local children = {}
	for _, area in pairs(self.areas) do
		if area.parent and area.parent == id then
			table.insert(children, area.id)
		end
	end
	return children
end

-- Checks if the user has sufficient privileges.
-- If the player is not a administrator it also checks
-- if the area intersects other areas that they do not own.
-- Also checks the size of the area and if the user already
-- has more than max_areas.
function areas:canPlayerAddArea(pos1, pos2, name)
	if minetest.check_player_privs(name, {areas=true}) then
		return true
	end

	-- Check self protection privilege, if it is enabled,
	-- and if the area is too big.
	if (not self.self_protection) or 
	   (not minetest.check_player_privs(name,
	   		{[areas.self_protection_privilege]=true})) then
		return false, "Self protection is disabled or you do not have"
				.." the necessary privilege."
	end

	if (pos2.x - pos1.x) > self.self_protection_max_size.x or
	   (pos2.y - pos1.y) > self.self_protection_max_size.y or
	   (pos2.z - pos1.z) > self.self_protection_max_size.z then
		return false, "Area is too big."
	end

	-- Check number of areas the user has and make sure it not above the max
	if self.self_protection then
		local count = 0
		for _, area in pairs(self.areas) do
			if area.owner == name then
				count = count + 1
			end
		end
		if count > self.self_protection_max_areas then
			return false, "You have reached the maximum amount"
					.." of areas that you are allowed to"
					.." protect."
		end
	end

	-- Check intersecting areas
	for _, area in pairs(self.areas) do
		if (area.pos1.x <= pos2.x and area.pos2.x >= pos1.x) and
		   (area.pos1.y <= pos2.y and area.pos2.y >= pos1.y) and
		   (area.pos1.z <= pos2.z and area.pos2.z >= pos1.z) then
			--Found an area intersecting with the suplied area
			if area.owner ~= name then
				return false, "The area intersects with an"
						.." area that you do not own."
			end
		end
	end

	return true, ""
end

-- Given a area returns a string in the format:
-- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
function areas:toString(area)
	local message = area.name..
		" ["..area.id.."]: "..area.owner.." "..
		minetest.pos_to_string(area.pos1).." "..
		minetest.pos_to_string(area.pos2)

	local children = areas:getChildren(id)
	if #children > 0 then
		message = message..
		" -> "..table.concat(children, ", ")
	end
	return message		
end

-- Returns a area given it's identifier
function areas:getAreaById(id)
	for _, area in pairs(self.areas) do
		if area.id == id then return area end
	end
end

-- Returns a table index for an area given it's identifier
function areas:getIndexById(id)
	for i, area in pairs(self.areas) do
		if area.id == id then return i end
	end
end

-- Re-order areas in table by their identifiers
function areas:sort()
	for k, area in pairs(self.areas) do
		if area.id ~= k then
			for _, subarea in pairs(self.areas) do
				if subarea.parent == area.id then
					subarea.parent = k
				end
			end
			area.id = k
		end
	end
end

-- Checks if a player owns an area or a parent of it
function areas:isAreaOwner(id, name)
	local cur = self:getAreaById(id)
	if cur and minetest.check_player_privs(name, {areas=true}) then
		return true
	end
	while cur do
		if cur.owner == name then
			return true
		elseif cur.parent then
			cur = self:getAreaById(cur.parent)
		else
			return false
		end
	end
	return false
end