-- Boilerplate to support localized strings if intllib mod is installed.
local S
if minetest.get_modpath("intllib") then
	S = intllib.Getter()
else
	-- If you don't use insertions (@1, @2, etc) you can use this:
	S = function(s) return s end
end


-- registers a quest for later use
--
-- questname is the name of the quest to identify it later
-- 	it should follow the naming conventions: "modname:questname"
-- quest is a table in the following format
-- 	{
--	  title, 		-- is shown to the player and should contain usefull information about the quest.
--	  description, 		-- a small description of the mod.
-- 	  max,			-- is the desired maximum. If max is 1, no maximum is displayed. defaults to 1
-- 	  autoaccept, 		-- is true or false, wether the result of the quest should be dealt by this mode or the registering mod.
-- 	  callback 		-- when autoaccept is true, at the end of the quest, it gets removed and callback is called.
--	                        -- function(playername, questname, metadata)
--	}
--
-- returns true, when the quest was successfully registered
-- returns falls, when there was already such a quest
function quests.register_quest(questname, quest)
	if (quests.registered_quests[questname] ~= nil) then
		return false -- The quest was not registered since there already a quest with that name
	end
	quests.registered_quests[questname] =
		{ title       = quest.title or S("missing title"),
		  description = quest.description or S("missing description"),
		  max         = quest.max or 1,
		  autoaccept  = quest.autoaccept or false,
		  callback    = quest.callback, }
	return true
end

-- starts a quest for a specified player
--
-- playername - the name of the player
-- questname  - the name of the quest, which was registered with quests.register_quest
-- metadata   - optional additional data
--
-- returns false on failure
-- returns true if the quest was started
function quests.start_quest(playername, questname, metadata)
	if (quests.registered_quests[questname] == nil) then
		return false
	end
	if (quests.active_quests[playername] == nil) then
		quests.active_quests[playername] = {}
	end
	if (quests.active_quests[playername][questname] ~= nil) then
		return false -- the player has already this quest
	end
	quests.active_quests[playername][questname] = {value = 0, metadata = metadata}

	quests.update_hud(playername)
	quests.show_message("new", playername, S("New quest:") .. " " .. quests.registered_quests[questname].title)
	return true
end

-- when something happens that has effect on a quest, a mod should call this method
-- playername is the name of the player
-- questname is the quest which gets updated
-- the quest gets updated by value
-- this method calls a previously specified callback if autoaccept is true
--
-- returns true if the quest is finished
-- returns false if there is no such quest or the quest continues
function quests.update_quest(playername, questname, value)
	if (quests.active_quests[playername] == nil) then
		quests.active_quests[playername] = {}
	end
	if (quests.active_quests[playername][questname] == nil) then
		return false -- there is no such quest
	end
	if (quests.active_quests[playername][questname].finished) then
		return false -- the quest is already finished
	end
	if (value == nil) then
		return false -- no value given
	end
	quests.active_quests[playername][questname]["value"] = quests.active_quests[playername][questname]["value"] + value
	if (quests.active_quests[playername][questname]["value"] >= quests.registered_quests[questname]["max"]) then
		quests.active_quests[playername][questname]["value"] = quests.registered_quests[questname]["max"]
		if (quests.registered_quests[questname]["autoaccept"]) then
			if (quests.registered_quests[questname]["callback"] ~= nil) then
				quests.registered_quests[questname]["callback"](playername, questname,
					quests.active_quests[playername][questname].metadata)
			end
			quests.accept_quest(playername,questname)
			quests.update_hud(playername)
		end
		return true -- the quest is finished
	end
	quests.update_hud(playername)
	return false -- the quest continues
end

-- When the mod handels the end of quests himself, e.g. you have to talk to somebody to finish the quest,
-- you have to call this method to end a quest
-- returns true, when the quest is completed
-- returns false, when the quest is still ongoing
function quests.accept_quest(playername, questname)
	if (quests.active_quests[playername][questname] and not quests.active_quests[playername][questname].finished) then
		if (quests.successfull_quests[playername] == nil) then
			quests.successfull_quests[playername] = {}
		end
		if (quests.successfull_quests[playername][questname] ~= nil) then
			quests.successfull_quests[playername][questname].count = quests.successfull_quests[playername][questname].count + 1
		else
			quests.successfull_quests[playername][questname] = {count = 1}
		end
		quests.active_quests[playername][questname].finished = true
		for _,quest in ipairs(quests.hud[playername].list) do
			if (quest.name == questname) then
				local player = minetest.get_player_by_name(playername)
				player:hud_change(quest.id, "number", quests.colors.success)
			end
		end
		quests.show_message("success", playername, S("Quest completed:") .. " " .. quests.registered_quests[questname].title)
		minetest.after(3, function(playername, questname)
			quests.active_quests[playername][questname] = nil
			quests.update_hud(playername)
		end, playername, questname)
		return true -- the quest is finished, the mod can give a reward
	end
	return false -- the quest hasn't finished
end

-- call this method, when you want to end a quest even when it was not finished
-- example: the player failed
--
-- returns false if the quest was not aborted
-- returns true when the quest was aborted
function quests.abort_quest(playername, questname)
	if (questname == nil) then
		return false
	end
	if (quests.failed_quests[playername] == nil) then
		quests.failed_quests[playername] = {}
	end
	if (quests.active_quests[playername][questname] == nil) then
		return false
	end
	if (quests.failed_quests[playername][questname] ~= nil) then
		quests.failed_quests[playername][questname].count = quests.failed_quests[playername][questname].count + 1
	else
		quests.failed_quests[playername][questname] = { count = 1 }
	end

	quests.active_quests[playername][questname].finished = true
	for _,quest in ipairs(quests.hud[playername].list) do
		if (quest.name == questname) then
			local player = minetest.get_player_by_name(playername)
			player:hud_change(quest.id, "number", quests.colors.failed)
		end
	end
	quests.show_message("failed", playername, S("Quest failed:") .. " " .. quests.registered_quests[questname].title)
	minetest.after(3, function(playername, questname)
		quests.active_quests[playername][questname] = nil
		quests.update_hud(playername)
	end, playername, questname)
end

-- get metadata of the quest if the quest exists, else return nil
function quests.get_metadata(playername, questname)
	if (quests.active_quests[playername] == nil or quests.active_quests[playername][questname] == nil) then
		return nil
	end
	return quests.active_quests[playername][questname].metadata
end

-- set metadata of the quest
function quests.set_metadata(playername, questname, metadata)
	if (quests.active_quests[playername] == nil or quests.active_quests[playername][questname] == nil) then
		return
	end
	quests.active_quests[playername][questname].metadata = metadata
end