-- ----------------------------------------------------------------------------
goalStack = {}
goalStack.stack = {}
goalStack.graph = {}
goalStack.index = 0
goalStack.empty = true
-- ----------------------------------------------------------------------------
function goalStack:dumpSteps()

	for i, g in ipairs(self.stack) do
		g:dumpSteps()
	end

end
-- ----------------------------------------------------------------------------
function goalStack:calcLockID(object)

	local lastID = -1
	local lastGoal = nil
	for i, goal in ipairs(self.stack) do
		if (goal:isStarted()) then
			if (goal:getStartID() > lastID) then
				lastGoal = goal
			end		
		end	
	end
	
	if (lastGoal) then
		local lockID = lastGoal:getLockID()
		if (object) then
			local lockIDExt = lockID .. "_" .. object:getID()
			if (ghf.hasString("TEXT_" .. lockIDExt)) then
				lockID = lockIDExt
			end
		end
		return lockID
	end
	
	print("WARNING: goalStack:calcLockID() could not find one!")
	
	return ""

end
-- ----------------------------------------------------------------------------
function goalStack:calcStep(topLayout)

	for i, g in ipairs(self.stack) do
		local r1 = nil
		local r2 = nil
		local r3 = nil		
		r1, r2, r3 = g:calcStep(topLayout)
		if (r1) then
			assert(r3)
			return r1, r2, r3
		end	
	end	
	
	return nil, nil, nil

end
-- ----------------------------------------------------------------------------
function goalStack:onTick(time)

	-- check for goals that need to finish
	-- pop them as well
	for i, goal in ipairs(self.stack) do
--		if (goal:isStarted()) then
			if (goal:shouldFinish()) then
				goal:finish()
				goal:pop()
				print("goalStack finished/popped " .. goal:name())
			end
--		end	
	end

	-- check if the stack is empty
	-- if so, increase the index and investigate
	-- possible new goals
	if (#(self.stack) == 0) then
	
		self.index = self.index + 1
		
		if (self.graph[self.index] ~= nil) then
			assert(self.graph[self.index].goals)
			-- push the new goals on the stack
			for i, goal in ipairs(self.graph[self.index].goals) do
				goal:push()
				print("goalStack pushed " .. goal:name())
			end
			-- start loading layouts
			if (self.graph[self.index].load) then
				for i, layout in ipairs(self.graph[self.index].load) do
					layout:loadResources()
				end
			end
			-- start unloading layouts
			if (self.graph[self.index].unload) then
				for i, layout in ipairs(self.graph[self.index].unload) do
					layout:unloadResources()
				end
			end			
		else
			-- no more goals
			-- which must mean we finished the game
			if (not self.empty) then
				print("goalStack has no more goals left...")
				self.empty = true
			end		
		end
	end
	
	-- check for goals that need to start
	local layout = layoutStack[#layoutStack]
	if (layout) then
		if ((not layout:isPuzzle()) or (layout:isPuzzle() and layout:isStarted())) then
			for i, goal in ipairs(self.stack) do
				if (not goal:isStarted()) then
					if (goal:shouldStart()) then
						goal:start()
						print("goalStack started " .. goal:name())
					end
				end
			end	
		end
	end
	
end
-- ----------------------------------------------------------------------------
-- don't call this function!
-- use goal:push()
function goalStack:push(goal)

	-- push it on the goal stack
	local stackPosition = nil
	for i, g in ipairs(self.stack) do
		if (g == goal) then
			stackPosition = i
			break
		end
	end
	
	if (not stackPosition) then
		-- add this goal to the stack
		table.insert(self.stack, goal)
	else
		-- error!
		assert(nil, "push was called on a Goal that's already in the stack. This shouldn't happen!")
	end
	
	self.empty = false

end
-- ----------------------------------------------------------------------------
-- don't call this function!
-- use goal:pop()
function goalStack:pop(goal)

	-- pop it from the goal stack
	-- which is not really a stack, since a pop doesn't necessarily
	-- remove the top element!
	local stackPosition = nil
	for i, g in ipairs(self.stack) do
		if (g == goal) then
			stackPosition = i
			break
		end
	end
	
	if (stackPosition) then
		-- remove this goal from the stack
		table.remove(self.stack, stackPosition)
	else
		-- error!
		assert(nil, "pop was called on a Goal that's not in the stack. This shouldn't happen!")
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onClickObject(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		-- NOTE:
		-- Calling _onClickObject here
		-- See goal.lua for comments!
		g:_onClickObject(layout, object, props, x, y)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onClickExit(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onClickExit(layout, object, props, x, y)
	end
	
end
-- ----------------------------------------------------------------------------
function goalStack:onClickPOI(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onClickPOI(layout, object, props, x, y) 
	end
	
end
-- ----------------------------------------------------------------------------
function goalStack:onClickHO(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onClickHO(layout, object, props, x, y)
	end
	
end
-- ----------------------------------------------------------------------------
function goalStack:onClickIO(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onClickIO(layout, object, props, x, y)
	end
	
end
-- ----------------------------------------------------------------------------
function goalStack:onClickFirefly(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onClickFirefly(layout, object, props, x, y)
	end
	
end
-- ----------------------------------------------------------------------------
function goalStack:onClickButton(layout, object, props, x, y)

	assert(layout)
	assert(layout.type == kLayout)	
	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onClickButton(layout, object, props, x, y)
	end
	
end
-- ----------------------------------------------------------------------------
function goalStack:onLayoutShow(layout)

	assert(layout)
	assert(layout.type == kLayout)	

	for i, g in ipairs(self.stack) do
		g:onLayoutShow(layout)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onLayoutHide(layout)

	assert(layout)
	assert(layout.type == kLayout)	

	for i, g in ipairs(self.stack) do
		g:onLayoutHide(layout)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onAnimationStart(animationName)

	for i, g in ipairs(self.stack) do
		g:onAnimationStart(animationName)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onAnimationFinish(animationName)

	for i, g in ipairs(self.stack) do
		g:onAnimationFinish(animationName)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onMovieStart(movieName)

	for i, g in ipairs(self.stack) do
		g:onMovieStart(movieName)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onMovieFinish(movieName)

	for i, g in ipairs(self.stack) do
		g:onMovieFinish(movieName)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onUseObject(selectedObject, targetObject, targetProps)

	assert(selectedObject)
	assert(selectedObject.type == kObject)
	assert(targetObject)
	assert(targetObject.type == kObject)	

	for i, g in ipairs(self.stack) do
		g:onUseObject(selectedObject, targetObject, targetProps)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onUsedAll(object, props)

	assert(object)
	assert(object.type == kObject)

	for i, g in ipairs(self.stack) do
		g:onUsedAll(object, props)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onStep()

	for i, g in ripairs(self.stack) do
		local r1 = nil
		local r2 = nil
		local path = nil		
		r1, r2, path = g:onStep()
		if (r1) then
			assert(path)
			return r1, r2, path
		end
	end
	
	return nil

end
-- ----------------------------------------------------------------------------
function goalStack:onHint()

	for i, g in ripairs(self.stack) do
		local r1 = nil
		local r2 = nil
		local path = nil		
		r1, r2, path = g:onHint()
		if (r1) then
			assert(path)
			return r1, r2, path
		end
	end
	
	return nil

end
-- ----------------------------------------------------------------------------
function goalStack:onSolve(layout)

	assert(layout)
	assert(layout.type == kLayout)

	for i, g in ipairs(self.stack) do
		g:onSolve(layout)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onSolveStart(layout)

	assert(layout)
	assert(layout.type == kLayout)

	for i, g in ipairs(self.stack) do
		g:onSolveStart(layout)
	end

end
-- ----------------------------------------------------------------------------
function goalStack:onSolveFinish(layout)

	assert(layout)
	assert(layout.type == kLayout)

	for i, g in ipairs(self.stack) do
		g:_onSolveFinish(layout)
	end

end