-- Copyright (c) 2013-18 rubenwardy. MIT.

local S, NS = awards.gettext, awards.ngettext

awards.registered_awards = {}
awards.on = {}
awards.on_unlock = {}

local default_def = {}

function default_def:run_callbacks(player, data, table_func)
	for i = 1, #self.on do
		local res = nil
		local entry = self.on[i]
		if type(entry) == "function" then
			res = entry(player, data)
		elseif type(entry) == "table" and entry.award then
			res = table_func(entry)
		end

		if res then
			awards.unlock(player:get_player_name(), res)
		end
	end
end

function awards.register_trigger(tname, tdef)
	assert(type(tdef) == "table",
			"Passing a callback to register_trigger is not supported in 3.0")

	tdef.name = tname
	for key, value in pairs(default_def) do
		tdef[key] = value
	end

	if tdef.type == "counted" then
		local old_reg = tdef.on_register

		function tdef:on_register(def)
			local tmp = {
				award  = def.name,
				target = def.trigger.target,
			}
			tdef.register(tmp)

			function def.getProgress(_, data)
				local done = math.min(data[tname] or 0, tmp.target)
				return {
					perc = done / tmp.target,
					label = S(tdef.progress, done, tmp.target),
				}
			end

			function def.getDefaultDescription(_)
				local n = def.trigger.target
				return NS(tdef.auto_description[1], tdef.auto_description[2], n, n)
			end

			if old_reg then
				return old_reg(tdef, def)
			end
		end

		function tdef.notify(player)
			assert(player and player.is_player and player:is_player())
			local name = player:get_player_name()
			local data = awards.player(name)

			-- Increment counter
			local currentVal = (data[tname] or 0) + 1
			data[tname] = currentVal

			tdef:run_callbacks(player, data, function(entry)
				if entry.target and entry.award and currentVal and
						currentVal >= entry.target then
					return entry.award
				end
			end)
		end

		awards["notify_" .. tname] = tdef.notify

	elseif tdef.type == "counted_key" then
		if tdef.key_is_item then
			tdef.watched_groups = {}
		end

		-- On award register
		local old_reg = tdef.on_register
		function tdef:on_register(def)
			-- Register trigger
			local tmp = {
				award  = def.name,
				key    = tdef:get_key(def),
				target = def.trigger.target,
			}
			tdef.register(tmp)

			-- If group, add it to watch list
			if tdef.key_is_item and tmp.key and tmp.key:sub(1, 6) == "group:" then
				tdef.watched_groups[tmp.key:sub(7, #tmp.key)] = true
			end

			-- Called to get progress values and labels
			function def.getProgress(_, data)
				data[tname] = data[tname] or {}

				local done
				if tmp.key then
					done = data[tname][tmp.key] or 0
				else
					done = data[tname].__total or 0
				end
				done = math.min(done, tmp.target)

				return {
					perc = done / tmp.target,
					label = S(tdef.progress, done, tmp.target),
				}
			end

			-- Build description if none is specificed by the award
			function def.getDefaultDescription(_)
				local n = def.trigger.target
				if tmp.key then
					local nname = tmp.key
					return NS(tdef.auto_description[1],
							tdef.auto_description[2], n, n, nname)
				else
					return NS(tdef.auto_description_total[1],
							tdef.auto_description_total[2], n, n)
				end
			end

			-- Call on_register in trigger type definition
			if old_reg then
				return old_reg(tdef, def)
			end
		end

		function tdef.notify(player, key, n)
			n = n or 1

			if tdef.key_is_item and key:sub(1, 6) ~= "group:" then
				local itemdef = minetest.registered_items[key]
				if itemdef then
					for groupname, _ in pairs(itemdef.groups or {}) do
						if tdef.watched_groups[groupname] then
							tdef.notify(player, "group:" .. groupname, n)
						end
					end
				end
			end

			assert(player and player.is_player and player:is_player() and key)
			local name = player:get_player_name()
			local data = awards.player(name)

			-- Increment counter
			data[tname] = data[tname] or {}
			local currentVal = (data[tname][key] or 0) + n
			data[tname][key] = currentVal
			if key:sub(1, 6) ~= "group:" then
				data[tname].__total = (data[tname].__total or 0) + n
			end

			tdef:run_callbacks(player, data, function(entry)
				local current
				if entry.key == key then
					current = currentVal
				elseif entry.key == nil then
					current = data[tname].__total
				else
					return
				end

				if current >= entry.target then
					return entry.award
				end
			end)
		end

		awards["notify_" .. tname] = tdef.notify

	elseif tdef.type and tdef.type ~= "custom" then
		error("Unrecognised trigger type " .. tdef.type)
	end

	awards.registered_triggers[tname] = tdef

	tdef.on = {}
	tdef.register = function(func)
		table.insert(tdef.on, func)
	end

	-- Backwards compat
	awards.on[tname] = tdef.on
	awards['register_on_' .. tname] = tdef.register
	return tdef
end

function awards.increment_item_counter(data, field, itemname, count)
	itemname = minetest.registered_aliases[itemname] or itemname
	data[field][itemname] = (data[field][itemname] or 0) + 1
end

function awards.get_item_count(data, field, itemname)
	itemname = minetest.registered_aliases[itemname] or itemname
	return data[field][itemname] or 0
end

function awards.get_total_keyed_count(data, field)
	return data[field].__total or 0
end

function awards.register_on_unlock(func)
	table.insert(awards.on_unlock, func)
end
