-- ----------------------------------------------------------------------------
-- new member variables that are accessible in each goal
Goal.mDone = false
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- overridden c++ functions
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
do

	local oldGoalOnPushed = Goal.onPushed
	function Goal:onPushed()

		-- push it
		goalStack:push(self)
		
	end

end
-- ----------------------------------------------------------------------------
do

	local oldGoalOnPopped = Goal.onPopped
	function Goal:onPopped()

		-- pop it
		goalStack:pop(self)

	end

end
-- ----------------------------------------------------------------------------
do

	local oldGoalOnStarted = Goal.onStarted
	function Goal:onStarted()
	end

end
-- ----------------------------------------------------------------------------
do

	local oldGoalOnFinished = Goal.onFinished
	function Goal:onFinished()
	end

end
-- ----------------------------------------------------------------------------
do
	local oldGoalShouldStart = Goal.shouldStart
	function Goal:shouldStart()
	
		return true
		
	end

end
-- ----------------------------------------------------------------------------
do
	local oldGoalShouldFinish = Goal.shouldFinish
	function Goal:shouldFinish()
	
		return self.mDone
		
	end

end
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- lua functions
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
function Goal:achieved()

	self.mDone = true

end
-- ----------------------------------------------------------------------------
-- This function exists because we want to update the steps after the click
-- is processed. When a goal overwrites the base onClickObject, we don't want
-- to have the requirement that they'll have to call the base. Therefore this
-- wrapper function.
function Goal:_onClickObject(layout, object, props, x, y)

	self:onClickObject(layout, object, props, x, y)

	if (self.mSteps) then
		for i, step in pairs(self.mSteps) do
			if (not step.done) then
				if (step.action and step.action == "click") then
					assert(step.object)
					if (step.object) then
						local objects = nil
						local objectGroup = nil
						if (step.object.__cpp) then
							-- it's a single object
							objects = {step.object}
							objectGroup = false
						else
							-- it's a group of objects
							objects = step.object
							objectGroup = true
						end
						assert(objects)

						local objectIndex = nil						
						for j, o in ipairs(objects) do
							if (object == o) then
								objectIndex = j
								break
							end
						end

						local isDone = false
						for j, o in ipairs(objects) do
							isDone = true
							
							if (object ~= o) then
								isDone = false
							end
							
							if (step.state) then
								if (step.state ~= object:getState()) then
									isDone = false
								end
							end
							
							if (step.started) then
								if (step.started ~= self:isStarted()) then
									isDone = false
								end
							end
							
							if (step.selection) then
								local selections = nil
								local selectionGroup = nil
								if (step.selection.__cpp) then
									-- it's a single object
									selections = {step.selection}
									selectionGroup = false
								else
									-- it's a group of objects
									selections = step.selection
									selectionGroup = true
								end
								assert(selections)
								assert(#objects == #selections)
								
								local selectionIndex = nil
								for k, selection in ipairs(selections) do
									if (selection == gSelection) then
										selectionIndex = k
										break
									end
								end
								
								-- if we found an index, 
								if (selectionIndex) then
									if (selectionGroup) then
										if (objectIndex) then
											-- delete object from objectgroup
											assert(objectGroup)
											table.remove(objects, objectIndex)
										
											-- delete selection from selectiongroup
											assert(selectionGroup)
											table.remove(selections, selectionIndex)
										
											-- we're done with this step only when both
											-- groups are empty
											isDone = ((#objects == 0) and (#selections == 0))
										end
									else
										-- still valid!
										-- no changes to isDone
									end
								else
									-- no valid selection, so not done
									isDone = false
								end
							end						
							
							if (isDone) then
								break
							end							
						end
						
						if (isDone) then
							print("(" .. self:name() .. ") completed step: " .. i)
							step.done = true
							break
						end
					end
				end
			end
		end	
	end

end
-- ----------------------------------------------------------------------------
function Goal:onClickObject(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onClickExit(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onClickPOI(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onClickHO(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onClickIO(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onClickFirefly(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onClickButton(layout, object, props, x, y)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onLayoutShow(layout)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onLayoutHide(layout)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onAnimationStart(animationName)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onAnimationFinish(animationName)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onMovieStart(movieName)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onMovieFinish(movieName)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onUseObject(selectedObject, targetObject, targetProps)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onUsedAll(object, props)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onStep()

	return self:calcStep()

end
-- ----------------------------------------------------------------------------
function Goal:onHint()

	layout = layoutStack[#layoutStack]
	if (layout) then
		for object, props in pairs(layout.mProps) do
			if (object:isSparkled()) then
				return object, nil, {}
			end
		end
	
		for object, props in pairs(layout.mHOs) do
			if (object:isActive()) then
				return object, nil, {}
			end
		end
		
		for object, props in pairs(layout.mIOs) do
			if (object:isActive()) then
				if (object.linked) then
					for i, link in ipairs(object.linked) do
						if (link:isSelected() or link:isFound()) then
							return object, link, {}
						end
					end
				end
			end
		end
	end

	return self:calcStep()

end
-- ----------------------------------------------------------------------------
-- searches through the steps and tries to find an object that we can return
-- * only inspects the first non-done step
-- * 'object = objectVar' or 'object = {objectVar1, objectVar2}' are allowed
-- * 'selection = selectionVar' or 'selection = {selectionVar1, selectionVar2}' are allowed
-- * @param topLayout if topLayout is given, only a step we can execute in topLayout
--                    is returned.
function Goal:calcStep(topLayout)

	if (self.mSteps) then
		for i, step in ipairs(self.mSteps) do
			if (not step.done) then
				if (step.action == "click") then
					if (step.object) then
						local objects = nil
						if (step.object.__cpp) then
							-- it's a single object
							objects = {step.object}
						else
							-- it's a group of objects
							objects = step.object
						end
						
						for j, object in ipairs(objects) do
							if (object:isLocked() or object:isActive()) then
								if ((object:getLayout() == topLayout) or (topLayout == nil)) then
									local sourceLayout = layoutStack[#layoutStack]
									local targetLayout = object:getLayout()
									local objectPath = dijkstraConstructActivePathToLayout(sourceLayout, targetLayout)
									if (objectPath) then
										if (step.state and step.selection) then
											assert(nil, "Not implemented!")
											
										elseif (step.state and step.started) then
											assert(nil, "Not implemented!")
											
										elseif (step.started and step.selection) then
											assert(nil, "Not implemented!")
											
										elseif (step.started) then
											assert(type(step.started) == "boolean")
											if (step.started) then
												if (self:isStarted()) then
													return object, nil, objectPath
												end
											else
												if (not self:isStarted()) then
													return object, nil, objectPath
												end
											end									
										elseif (step.state) then
											assert(type(step.state) == "number")
											if (object:getState() == step.state) then
												print("(" .. self:name() .. ") found hint object: " .. object:name() .. " (required: " .. object:getStateString() .. ")")
												return object, nil, objectPath						
											end						
										elseif (step.selection) then
											local selections = nil
											if (step.selection.__cpp) then
												-- it's a single object
												selections = {step.selection}
											else
												-- it's a group of objects
												selections = step.selection
											end
											assert(#objects == #selections)
		
											for k, selection in ipairs(selections) do
												if (selection:isFound() or selection:isSelected()) then
													print("(" .. self:name() .. ") found 2 hint objects: " .. object:name() .. "/" .. selection:name())
													return object, selection, objectPath
												end
											end
										elseif (step.object) then
											print("(" .. self:name() .. ") found hint object: " .. object:name() .. " (" .. object:getState() .. ")")
											return object, nil, objectPath
										end
									end
								end
							end
						end
					end
				end
				
				if (step.action == "solve") then
					if (step.layout) then
						local isOk = true
						
						if (step.started) then
							assert(type(step.started) == "boolean")
							if (step.started) then
								if (not self:isStarted()) then
									isOk = false
								end
							else
								if (self:isStarted()) then
									isOk = false
								end
							end 
						end
						
						if (isOk) then
							if ((step.layout == topLayout) or (topLayout == nil)) then					
								local sourceLayout = layoutStack[#layoutStack]
								local targetLayout = step.layout
								local objectPath = dijkstraConstructActivePathToLayout(sourceLayout, targetLayout)
								if (objectPath) then
									local layout = step.layout
									assert(type(layout) == "table")
									return layout, nil, objectPath
								end
							end
						end
					end				
				end
				
				-- we've found a non-done step
				break
			end
		end	
	end
	
	print("(" .. self:name() .. ") none found!")
	
	return nil, nil, nil	

end
-- ----------------------------------------------------------------------------
function Goal:onSolve(layout)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:onSolveStart(layout)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:_onSolveFinish(layout)

	self:onSolveFinish(layout)

	if (self.mSteps) then
		for i, step in pairs(self.mSteps) do
			if (not step.done) then
				if (step.action and step.action == "solve") then
					if (step.layout == layout) then
						print("(" .. self:name() .. ") completed step: " .. i)
						step.done = true
						break
					end
				end
			end
		end	
	end

end
-- ----------------------------------------------------------------------------
function Goal:onSolveFinish(layout)
	-- no code here
end
-- ----------------------------------------------------------------------------
function Goal:dumpSteps()

	local text = ""
	
	text = text .. self:name()
	text = text .. ":\n"
	
	if (self.mDone) then
		text = text .. "*: isDone -> true\n"
	else
		text = text .. "*: isDone -> false\n"
	end
	
	if (self:isStarted()) then
		text = text .. "*: isStarted -> true\n"
	else
		text = text .. "*: isStarted -> false\n"
	end	
	
	if (self.mSteps) then
		for i, step in pairs(self.mSteps) do
			text = text .. i .. ": "
			for k,v in pairs(step) do
				text = text .. k
				text = text .. "->"
				text = text .. self:dumpStepVar(v)
			end
			text = text .. "\n"
		end
	end
	
	print(text)

end
-- ----------------------------------------------------------------------------
function Goal:dumpStepVar(v)

	local text = ""
	
	if (type(v) == "string") then
		text = text .. v
	elseif (type(v) == "boolean") then
		if (v == true) then
			text = text .. "true"
		else
			text = text .. "false"
		end
	elseif (type(v) == "number") then
		text = text .. v
	elseif (v.type == kObject) then
		text = text .. v:name()
	elseif (v.type == kLayout) then
		text = text .. v:name()
	else
		-- let's assume it's a list
		-- recusive!
		
		text = text .. "{"
		for _,cv in ipairs(v) do
			text = text .. self:dumpStepVar(cv)			
		end
		text = text .. "}"
	end
	text = text .. ", "
	
	return text

end