--[[
Methods that are easier to define in Lua.
This also has the superclass setup.

09 Jun 09 drd Named animation sequences
13 Nov 08 drd Better error handling in doEvery()
18 Aug 08 drd Animation sequences
22 Apr 08 drd Created

--]]

ghf = ghf or {}
ghf.LuaObject = getClass("LuaObject")
ghf.GameNode = getClass("GameNode")
ghf.Sprite = getClass("Sprite")
ghf.Button = getClass("Button")
ghf.TextButton = getClass("TextButton")
ghf.Label = getClass("Label")
ghf.Dialog = getClass("Dialog")
ghf.ParticleEffect = getClass("ParticleEffect")

function ghf.LuaObject:copySetupTable(desc)
	function checkAccessor(acc, k)
		return forEachMetatable(self, function(meta)
			local acc_t = rawget(meta, acc)
			if isTable(acc_t) then
				return rawget(acc_t, k)
			end
		end)
	end

	local skipKeys = self.skipKeys or {}
	table.copy_into(skipKeys, {skipKeys=true, class=true, classname=true, super=true})
	table.copy_into(skipKeys, desc.skipKeys)

	for k,v in pairs(desc) do
		if not skipKeys[k] then
			if checkAccessor("__getters", k) ~= nil and
				checkAccessor("__setters", k) == nil then
				print(
					"Assigning the key '"..tostring(k).."' "..
					"to an object that has a getter but not a setter with that name. "..
					"This is not allowed because it leads to unpredictable behavior.\n"..
					"self = "..tostring(self).."\nvalue = "..tostring(v))
			end

			self[k] = v
		end
	end
end

--[[
Sample usage:
	sprite:animate(
		{alpha = 0.5, duration = 100},					 -- Over 100 ms
		{x = 10, y = 10, style="easeInOut", duration = 50}, -- (ModifierLocationLine)
		{delay = 10},
		{dx = -10, dy = 20},							 -- Move immediately by the delta (ModifierLocation)
		{frame = 3},									 -- Cell#
		{frame = {...}, loop=2, direction="switch"},	 -- Cell#s (ModifierFrame) !!! not implemented
		{delay = waitFunction},							 -- Delay until waitFunction(sprite) returns false
		{												 -- Nested table means the two actions happen at the same time
			{sprite = s1, visible = true},
			{sprite = s2, visible = false}
		},
		{rotation = 45},
		{drotation = -45, duration = 1000},
		{skew = 0.1, duration = 500},
		{dskew = 0.05, duration = 200},
		{skewx = 0.5, skewy = -0.1, duration = 200},
		{path={...}, loop=true},						 -- (ModifierLocationPolygonicPath) !!! not implemented
		{call = function() print("Still animating") end},
		{call = function(aSprite) aSprite:removeFromParent() end},
		{command = "doSomething"},						 -- Receive this in HandleCommand(command, sprite)
		{command = "doSomething", target = dialog}
	)

See <https://wiki.ra.prognet.com/display/GHST/Lua+Animation+Sequences> for full documentation.

Note that although this is a Sprite method, this is only for convenience; modifiers
will be added to AnimTree and not a Sprite
--]]
function ghf.Sprite:animate(...)
	local nArgs = select('#', ...)

	-- See if we're passed a single argument which is an array of animations
	local firstArg = select(1, ...)
	if nArgs == 1 then
		if type(firstArg) == "table" then
			local subArg = firstArg[1]
			if type(subArg) == "table" then
				self:animate(unpack(firstArg))
				return
			end
		end
	end

	local firstIndex = 1
	local animationName
	if isString(firstArg) then
		animationName = firstArg
		firstIndex = 2
	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

	self:beginAnimation(animationName)
	for i = firstIndex, nArgs do
		local arg = select(i, ...) -- Get i-th parameter
		if arg == nil then
			print("ERROR: nil action")
		else
			if #arg == 0 then
				self:addSequentialAnimation(i, arg)
			else
				self:beginSimultaneousAnimation()
				for j,anim in ipairs(arg) do
					if anim == nil then
						print("ERROR: nil action in simultaneous group")
					else
						self:addSequentialAnimation(0, anim)
					end
				end
				self:endSimultaneousAnimation()
			end
		end
	end
end

function ghf.Sprite:doEvery(aMilliseconds, aFunction)
	if aMilliseconds < 0 then
		error("doEvery time can't be negative (" .. aMilliseconds .. ")")
	end

	self:animate(
		{delay = aMilliseconds},
		{call = function()
			local success, d = pcall(aFunction)
			if success == false then
				print("doEvery:", d)
				error(d)
			end
			if d == false then
				return
			end
			if d == nil then
				d = aMilliseconds
			end
			self:doEvery(d, aFunction)
		end}
	)
end
