
-- ************************************ TaskSystem ************************************ --
TaskSystem = getClass("TaskSystem")

function TaskSystem:addTasks(t)
	t = self:wrapSingleTable(t)
	for k,v in pairs(t) do
		if (isTable(v)) then
			self:addTask(v)
		end
	end
end

function TaskSystem:wrapSingleTable(t)
	assert(isTable(t))
	for k,v in pairs(t) do
		if (type(k) ~= "number" or type(v) ~= "table") then
			return { t }
		end
	end
	return t
end

function TaskSystem:onTaskStart(task, ...)
end

function TaskSystem:onTaskFinish(task, ...)
end

function TaskSystem:onTaskCancel(task, ...)
end

function TaskSystem:getAnimationDesc(animId)
	assert(isString(animId), "Must provide an animation id")
	if (animationsDesc) then
		for k,v in pairs(animationsDesc) do
			if (isTable(v) and v.id == animId) then
				return v
			end
		end
	end
end

-- Create an animation stored in the animations table for this level
-- The named parameter table can contain value keyword substitutes
function TaskSystem:createAnimation(animId, paramTable)

	local animTable = self:getAnimationDesc(animId)
	if (not isTable(animTable))then
		error("Provided no table to create the animation with. id = "..tostring(animId))
		return
	end

	if (isString(animTable.name)) then
		--print("[TaskSystem:createAnimation] Creating animation: "..animTable.name)
	end

	-- Try to find an value given a certain value
	-- with the help of a parameters table
	function resolveValue(v, paramTable, allowParse)
		allowParse = allowParse ~= false

		if (isTable(paramTable) and paramTable[v] ~= nil) then
			v = paramTable[v]

			-- Return the table itself
			-- instead of creating a character
			if (isTable(v)) then
				return v
			end
		end

		-- Check if we're dealing with a string
		if (isString(v)) then

			if (v == "hero") then
				return hero

			elseif (v == "resident") then
				return resident

			else
				-- Return a character from the level
				local obj = level:getSpriteExt(v)
				if (obj ~= nil) then return obj end

				if (allowParse) then
					-- Try to parse the string
					local f = loadstring("return "..v)
					if (isCallable(f)) then
						local success, r = pcall(f)
						if (success and r ~= nil) then
							local obj = resolveValue(r, paramTable, false)
							if (obj) then
								return obj
							end
							return r
						end
					end
				end

			end
		elseif (isTable(v)) then
			-- Return a newly created character
			-- (which is added to the level immediately)
			return level:createCharacter(v)

		end

		return v
	end

	-- Create a (sub) tree from an animation description
	function createTaskTree(root, treeDesc, list, values, simultaneous)

		local curTask = root

		-- Loop over all the tasks in the animation
		for k,v in pairs(treeDesc) do

			-- Determine whether we should be adding the next task
			-- alongside or after the previous task
			local isRoot = curTask == root

			-- If the tasks are located another level deep
			if (isTable(v)) then

				-- Create a new group
				local group
				if (isRoot) then
					group = curTask:addGroup()
				elseif (simultaneous) then
					group = curTask:alsoGroup()
				else
					group = curTask:atEndGroup()
				end

				-- Call this function recursively
				createTaskTree(group, v, list, values, not simultaneous)

				-- Set the created group as the current task
				curTask = group

			-- Look up the task definition in the list of tasks
			else
				-- This needs to be an index into the animation description list
				assert(isNumber(v), "This is not a number: "..tostring(v).." ("..tostring(k)..")")

				-- Make sure the provided number actually points to
				-- an animtion description in the list
				if (not isTable(list[v])) then
					print("Warning: "..v.." is not a valid index in animation '"..animTable.name.."'")

				else
					-- Make a copy of the definition
					local desc = table.copy(list[v])

					-- Replace value keywords with actual values
					for dk,dv in pairs(desc) do
						local value = values[dv]
						if (value ~= nil and not isNumber(dv)) then
							desc[dk] = value
							--print("dk: "..tostring(dk).." desc[dk]: "..tostring(desc[dk].." dv: "..tostring(dv)))
						end
					end

					-- Actually create the task and add the task to the current tree
					local task
					if (isRoot) then
						task = curTask:add(desc)
					elseif (simultaneous) then
						task = curTask:also(desc)
					else
						task = curTask:atEnd(desc)
					end

					-- Set the created task as the current task
					curTask = task
				end
			end

			first = false
		end
	end

	-- Resolve all values using predefined keywords and
	-- the parameter table
	local values = {}
	if (isTable(animTable.actors)) then
		for k,v in pairs(animTable.actors) do
			values[k] = resolveValue(v, paramTable)
			--print("Actor: "..k.." = "..tostring(values[k]))
			assert(values[k] ~= nil, tostring(v).." is not a value ("..tostring(k)..")")
		end
	end

	-- Create a new animation in the task system
	local root = self:newAnimation(animTable.name, animTable.exclusive, values)

	root.musicMoodStart = animTable.musicMoodStart
	root.musicMoodEnd = animTable.musicMoodEnd

	-- Build the animation tree
	createTaskTree(root, animTable.tree, animTable.list, values, false)

	return root
end

function musicMoodString(str)
	if (str == "-") then
		return nil
	end
	return str
end

function TaskSystem:onAnimationStarted(anim)
	if (level) then
		level:onAnimationStarted(anim)
	end
end

function TaskSystem:onAnimationEnded(anim)
	if (level) then
		level:onAnimationEnded(anim)
	end
end
