local _pts = minetest.pos_to_string
function minetest.pos_to_string(pos)
	if not pos then
		return "(-,-,-)"
	end
	return _pts(pos)
end

local elapsed_time = 0

-- Makes sure that force load areas are handled correctly
function ForceloadManager(filetoopen, hide_file_errors)
	local blocks = {}
	if filetoopen ~= nil then
		local file = io.open(filetoopen, "r")
		if file then
			local table = minetest.deserialize(file:read("*all"))
			file:close()
			if type(table) == "table" then
				blocks = table
			end
		elseif not hide_file_errors then
			minetest.log("error", "File "..filetoopen.." does not exist!")
		end
	end
	for i = 1, #blocks do
		if not minetest.forceload_block(blocks[i]) then			
			minetest.log("error", "Failed to load block " .. minetest.pos_to_string(blocks[i]))
		end
	end
	return {
		_blocks = blocks,
		load = function(self, pos)
			if minetest.forceload_block(pos) then
				table.insert(self._blocks, vector.new(pos))
				return true
			end
			minetest.log("error", "Failed to load block " .. minetest.pos_to_string(pos))
			return false
		end,
		unload = function(self, pos)
			for i = 1, #self._blocks do
				if vector.equals(pos, self._blocks[i]) then					
					minetest.forceload_free_block(pos)
					table.remove(self._blocks, i)
					return true
				end
			end
			return false
		end,
		save = function(self, filename)
			local file = io.open(filename, "w")
			if file then
				file:write(minetest.serialize(self._blocks))
				file:close()
			end
		end,
		verify = function(self)
			return self:verify_each(function(pos, block)				
				local name = "ignore"
				if block ~= nil then
					name = block.name
				end

				if name == "ignore" then	
					if not pos.last or elapsed_time > pos.last + 15 then
						pos.last = elapsed_time
						if not minetest.forceload_block(pos) then							
							minetest.log("error", "Failed to force load " .. minetest.pos_to_string(pos))
							pos.remove = true
						end
					end
					return false
				elseif name == "forceload:anchor" then
					pos.last = elapsed_time
					return true
				else	
					minetest.log("error", minetest.pos_to_string(pos) .. " shouldn't be loaded")
					pos.remove = true
					return false		
				end
			end)
		end,
		verify_each = function(self, func)
			local not_loaded = {}			
			for i = 1, #self._blocks do
				local res = minetest.get_node(self._blocks[i])
				if not func(self._blocks[i], res) then
					--[[table.insert(not_loaded, {
						pos = self._blocks[i],
						i = i,
						b = res })]]--
				end
			end
			return not_loaded
		end,
		clean = function(self)
			local i = 1
			while i <= #self._blocks do
				if self._blocks[i].remove then
					minetest.forceload_free_block(self._blocks[i])
					table.remove(self._blocks, i)
				else
					i = i + 1
				end
			end
		end
	}
end

local flm = ForceloadManager(minetest.get_worldpath().."/flm.json", true)

minetest.register_privilege("forceload", "Allows players to use forceload block anchors")

minetest.register_node("forceload:anchor",{
	description = "Block Anchor",
	walkable = false,
	tiles = {"forceload_anchor.png"},
	groups = {cracky = 3, oddly_breakable_by_hand = 2},
	after_destruct = function(pos)
		flm:unload(pos)
		flm:save(minetest.get_worldpath().."/flm.json")
	end,
	after_place_node = function(pos, placer)
		if not minetest.check_player_privs(placer:get_player_name(),
				{forceload = true}) then
			minetest.chat_send_player(placer:get_player_name(), "The forceload privilege is required to do that.")
		elseif flm:load(pos) then			
			flm:save(minetest.get_worldpath().."/flm.json")
			return
		end		
		minetest.set_node(pos, {name="air"})
		return true
	end
})

minetest.register_craft({
	output = "forceload:anchor",
	recipe = {
		{"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"},
		{"default:mese_crystal", "wool:blue", "default:mese_crystal"},
		{"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"}
	}
})


local count = 0
minetest.register_globalstep(function(dtime)
	count = count + dtime
	elapsed_time = elapsed_time + dtime
	if count > 5 then
		count = 0
		--print("Verifying...")
		flm:verify()
		flm:clean()
	end
end)