-- ----------------------------------------------------------------------------
animationsList = {}
animationsSave = {}
animationsLoad = {}
-- ----------------------------------------------------------------------------
-- we write all animations in lua
-- but we'd like to respond on them as well
-- so wrap the ghf.Sprite.animation function
-- pretty smart imo!
-- ----------------------------------------------------------------------------
kAnimationBusy = 0
kAnimationIdle = 1
-- ----------------------------------------------------------------------------
function onAnimationStart(layout, name, object)

	assert(layout)

	if (string.find(name, "_blocking")) then
		-- info
		print("(blocking animation) start: " .. name .. " in " .. layout:name())
	end

	-- busy in list
	animationsList[name] = kAnimationBusy
	
	-- dispatch to layout
	layout:animationStart(name)
	
	-- dispatch to goalStack
	goalStack:onAnimationStart(name)	

end
-- ----------------------------------------------------------------------------
function onAnimationFinish(layout, name, object)

	assert(layout)

	if (string.find(name, "_blocking")) then
		-- info
		print("(blocking animation) finish: " .. name .. " in " .. layout:name())
	end

	-- remove the animation from the saved list
	local saveIndex = nil
	for i, entry in ipairs(animationsSave) do
		if (entry.object == object) then
			if (entry.name == name) then
				saveIndex = i
				break
			end
		end
	end
	if (saveIndex) then
		table.remove(animationsSave, saveIndex)
	end

	-- idle in list
	animationsList[name] = kAnimationIdle

	-- dispatch to layout
	layout:animationFinish(name)
	
	-- dispatch to goalStack
	goalStack:onAnimationFinish(name)	
	
end
-- ----------------------------------------------------------------------------
function doAnimation(...)
	local	nArgs = select('#', ...)
	
	local aSprite = select(1, ...)

	-- See if we're passed a single argument which is an array of animations
	local firstArg = select(2, ...)
	if nArgs == 1 then
		if type(firstArg) == "table" then
			local	subArg = firstArg[1]
			if type(subArg) == "table" then
				doAnimation(aSprite, unpack(firstArg))
				return
			end
		end
	end
	
	local firstIndex = 2
	local animationName = "UnnamedAnimation"
	if type(firstArg) == "string" then
		animationName = firstArg
		firstIndex = 3
	end	

	-- Validate arguments
	for i = firstIndex, nArgs do
		local arg = select(i, ...)	-- Get i-th parameter
		if type(arg) ~= "table" then
			error("Must specify each animation as a table", 2)
		end
	end		

	-- here is where we start
	-- first build a list of all the animation arguments
	local animationArgs = {}
	for i = firstIndex, nArgs do
		local arg = select(i, ...)	-- Get i-th parameter
		table.insert(animationArgs, arg)
	end
	
	-- add one callback to the front
	-- add one callback to the back
	
	-- get the layout that should be notified
	local layout = nil
	if (aSprite.type) then
		if (aSprite.type == kObject) then
			layout = aSprite:getLayout()
		end
		if (aSprite.type == kLayout) then
			layout = aSprite
		end	
	end
	assert(layout)
	
-- 	don't call onAnimationStart here... see below!	
--	table.insert(animationArgs, 1, { call = function(aSprite) onAnimationStart(layout, animationName, aSprite) end } )
	table.insert(animationArgs, { call = function(aSprite) onAnimationFinish(layout, animationName, aSprite) end } )
	
	-- now call the old function to set everything up
	ghf.Sprite.animate(aSprite, animationName, unpack(animationArgs))

	-- call onAnimationStart manually
	-- otherwise when chaining animations, there's one tick between onAnimationStart/onAnimationFinish
	-- where stepping/input is allowed
	onAnimationStart(layout, animationName)
	
	-- save the animation
	-- only non blocking animations are worth saving
	if (aSprite.type) then
		if (not string.find(animationName, "_blocking")) then	
			local savedAnimation = {}
			savedAnimation.object = aSprite
			savedAnimation.name = animationName
			table.remove(animationArgs)
			savedAnimation.args = animationArgs
			table.insert(animationsSave, savedAnimation)
		end
	end

end
-- ----------------------------------------------------------------------------
if (oldCancelAnimation == nil) then
	oldCancelAnimation = ghf.cancelAnimation
	ghf.cancelAnimation = function(name)
		print("(cancel animation) " .. name)
		animationsList[name] = kAnimationIdle
		oldCancelAnimation(name)
	end
end
-- ----------------------------------------------------------------------------
function isAnimationFinished(name)
	assert(type(name) == "string")
		
	if (animationsList[name] ~= nil) then
		return (animationsList[name] == kAnimationIdle)
	end
	
	return false

end
-- ----------------------------------------------------------------------------
function isAnimationStarted(name)
	assert(type(name) == "string")
	
	if (animationsList[name] ~= nil) then
		return (animationsList[name] == kAnimationBusy)
	end
	
	return false	
	
end
-- ----------------------------------------------------------------------------
function isBlockingAnimationBusy()

	for name, state in pairs(animationsList) do
		if (string.endsWith(name, "_blocking")) then
			if (state == kAnimationBusy) then
				return true
			end		
		end	
	end
	
	return false

end
-- ----------------------------------------------------------------------------
function loadAnimations()

	for i, entryLoad in ipairs(animationsLoad) do
		local object = entryLoad.object
		local name = entryLoad.name
		local args = entryLoad.args	
		assert(object)
		assert(object.type)
		assert(name)
		assert(args)
		
		local ignore = false
		-- check if we're done with the object
		-- if so, the animation is an obsolete entry
		-- that got removed in SwitchState(kDone/kUsed)
		if (object.type == kObject) then
			if (object:isDone() or object:isUsed()) then	
				ignore = true
			end	
		end
		
		if (not ignore) then
			-- see if this entry already exists in the save map
			-- if so, it already got started
			local entryIndex = nil
			for j, entrySave in ipairs(animationsSave) do
				if (entrySave.name == name) then
					if (entrySave.object == object) then
						entryIndex = j
						break
					end
				end
			end
			
			-- restart the animation if needed
			if (not entryIndex) then
				-- info
				print("(animation loaded) restarted animation " .. name .. " on " .. object:name())
			
				-- call
				object:animate(name, unpack(args))
			end	
		end
	end

end
