local paraph = {}
local base_param = ...
local sw,sh,scx,scy,songdir,rr,am,bt,hs,pe,hr,ease,wratio,hratio = base_param.sw,base_param.sh,base_param.scx,base_param.scy,base_param.songdir,base_param.rr,base_param.am,base_param.bt,base_param.hs,base_param.pe,base_param.hr,base_param.ease,base_param.wratio,base_param.hratio

paraph.SM = function( arg, duration )
	local msg

	local TableToString_Recursive = function(t, name, indent)
		local tableList = {}
		local table_r

		table_r = function(t, name, indent, full)
			local id = not full and name or type(name)~="number" and tostring(name) or '['..name..']'
			local tag = indent .. id .. ' = '
			local out = {}	-- result

			if type(t) == "table" then
				if tableList[t] ~= nil then
					table.insert(out, tag .. '{} -- ' .. tableList[t] .. ' (self reference)')
				else
					tableList[t]= full and (full .. '.' .. id) or id
					if next(t) then -- Table not empty
						table.insert(out, tag .. '{')
						for key,value in pairs(t) do
							table.insert(out,table_r(value,key,indent .. '|    ',tableList[t]))
						end
						table.insert(out,indent .. '}')
					else
						table.insert(out,tag .. '{}')
					end
				end
			else
				local val = type(t)~="number" and type(t)~="boolean" and '"'..tostring(t)..'"' or tostring(t)
				table.insert(out, tag .. val)
			end

			return table.concat(out, '\n')
		end

		return table_r(t,name or 'Value',indent or '')
	end
	-- if a table has been passed in, recursively stringify the table's keys and values
	if type( arg ) == "table" then
		msg = TableToString_Recursive(arg)

	-- otherwise, Lua's standard tostring() should suffice
	else
		msg = tostring(arg)
	end

	-- SCREENMAN:SystemMessage() is effectively a convenience function for broadcasting
	-- "SystemMessage" with certain parameters.  see: ScreenManager.cpp
	-- let's broadcast directly using MESSAGEMAN so that we can also pack in a duration
	-- value (how long to display the SystemMessage for) if so desired
	MESSAGEMAN:Broadcast("SystemMessage", {Message=msg, Duration=duration})
end

paraph.table_add = function(a,b)
	if type(a) == 'number' and type(b) == 'number' then
		return a+b
	elseif type(a) == 'table' and type(b) == 'table' and #a == #b then
		local c = {}
		for i = 1, #a do
			c[i] = a[i]+b[i]
		end
		return c
	else
		SCREENMAN:SystemMessage('Dimension incosistency in table arithmetics')
		return 0
	end
end

paraph.table_sub = function(a,b)
	if type(a) == 'number' and type(b) == 'number' then
		return a-b
	elseif type(a) == 'table' and type(b) == 'table' and #a == #b then
		local c = {}
		for i = 1, #a do
			c[i] = a[i]-b[i]
		end
		return c
	else
		SCREENMAN:SystemMessage('Dimension incosistency in table arithmetics')
		return 0
	end
end

paraph.table_mul = function(a,b)
	if type(a) == 'number' and type(b) == 'number' then
		return a*b
	elseif type(a) == 'table' and type(b) == 'number' then
		local c = {}
		for i = 1, #a do
			c[i] = a[i]*b
		end
		return c
	elseif type(a) == 'number' and type(b) == 'table' then
		local c = {}
		for i = 1, #b do
			c[i] = b[i]*a
		end
		return c
	elseif type(a) == 'table' and type(b) == 'table' and #a == #b then
		local c = {}
		for i = 1, #a do
			c[i] = a[i]*b[i]
		end
		return c
	else
		SCREENMAN:SystemMessage('Dimension incosistency in table arithmetics')
		return 0
	end
end

paraph.table_div = function(a,b)
	if type(a) == 'number' and type(b) == 'number' then
		return a/b
	elseif type(a) == 'table' and type(b) == 'number' then
		local c = {}
		for i = 1, #a do
			c[i] = a[i]/b
		end
		return c
	elseif type(a) == 'number' and type(b) == 'table' then
		local c = {}
		for i = 1, #b do
			c[i] = a/b[i]
		end
		return c
	elseif type(a) == 'table' and type(b) == 'table' and #a == #b then
		local c = {}
		for i = 1, #a do
			c[i] = a[i]/b[i]
		end
		return c
	else
		SCREENMAN:SystemMessage('Dimension incosistency in table arithmetics')
		return 0
	end
end

paraph.fmod = function(int, base)
	while int <= 0 do
		int = int + base
	end
	return int - math.floor(int/base)*base
end

paraph.BitmapText = function(str, param)
	local tot_len, len = 44, {}
	local st = {}
	-- This function requires the index of sprite.
	-- Thus " is inacceptable
	local char = param.char and param.char or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,.+-/*{}_# :'()=[]"
	local font = param.Font and param.Font or 'Consolas'
	local name = param.Name and param.Name or 'BitmapText'
	local mode = param.Mode and param.Mode or 'Normal'
	local size = param.Size and param.Size or 64
	local blend = param.Blend and param.Blend or 'Normal'
	local color = param.Color and param.Color or {1,1,1,1}
	local align = param.Align and param.Align or 'center'
	for i = 1, string.len(str) do
		local c = string.sub(str,i,i)
		local n, v = string.find(char, c, 1, true)-1
		if not n then n = string.len(char)-1 end
		len[i] = tot_len
		st[i] = n
		tot_len = tot_len + 44
	end
	local aster = false
	if string.find(mode, '*') then
		mode = string.gsub(mode, '*', '')
		len[#len+1] = tot_len
		tot_len = tot_len + 44
		aster = true
	end
	if align == 'left' then tot_len = 0 end
	if align == 'right' then tot_len = tot_len*2 end

	local af = Def.ActorFrame{Name=name,InitCommand=function(self) self:basezoom(size/64) end}

	if mode == 'Normal' then
		for i = 1, string.len(str) do
			af[#af+1] = Def.Sprite{
				Name='c'..i,
				Texture='texture/_'..font,
				InitCommand=function(self)
					self:animate(0):setstate(st[i]):x(len[i]-tot_len/2):diffuse(unpack(color)):blend(blend)
				end,
			}
		end
	elseif mode == 'CountOut' then
		local delay = param.Delay and param.Delay or bt/4
		local time = param.Time and param.Time or bt
		local num = param.Num and param.Num or 8
		local base = string.len(char)-1
		af['InCommand'] = function(self)
			for i = 1, base+1 do
				if self:GetChild('c'..i-1) then
					self:GetChild('c'..i-1):sleep(delay*(i-1)):aux(num-1):queuecommand('CIn')
				end
			end
		end
		af['OutCommand'] = function(self)
			for i = 1, base+1 do
				if self:GetChild('c'..i-1) then
					self:GetChild('c'..i-1):sleep(delay*(i-1)):queuecommand('COut')
				end
			end
		end
		for i = 1, string.len(str) do
			af[#af+1] = Def.Sprite{
				Name='c'..i,
				Texture='texture/_'..font,
				InitCommand=function(self)
					self:animate(0):x(len[i]-tot_len/2):diffuse(unpack(color)):diffusealpha(0):blend(blend)
				end,
				CInCommand=function(self)
					self:setstate(st[i]):diffusealpha(color[4])
				end,
				COutCommand=function(self)
					self:setstate(paraph.fmod(st[i]+self:getaux(),base)):aux(self:getaux()+1)
					if self:getaux() <= num then
						self:sleep(time/num):queuecommand('COut')
					else
						self:linear(time/num):diffusealpha(0)
					end
				end,
			}
		end
	end
	if aster then
		af[#af+1] = Def.Sprite{
			Name='c'..0,
			Texture='texture/aster_'..font,
			Frames = {
				{Frame=0, Delay=bt/4},
				{Frame=1, Delay=bt/4},
				{Frame=2, Delay=bt/4},
				{Frame=3, Delay=bt/4},
			},
			InitCommand=function(self)
				self:x(len[#len]-tot_len/2):diffuse(unpack(color)):blend(blend)
			end,
		}
	end
	return af
end

paraph.AFT = function(param)
	local width = param.Width and param.Width or SCREEN_WIDTH
	local height = param.Height and param.Height or SCREEN_HEIGHT
	local depth = param.Depth and param.Depth or true
	local alpha = param.Alpha and param.Alpha or true
	local command = param.Command and param.Command or 'InitializeCommand'
	local InitCheck = false
	if command == 'InitCommand' then InitCheck = true command = 'AFTCommand' end
	local aft = Def.ActorFrameTexture{
		InitCommand=function(self)
			self:SetWidth(width):SetHeight(height):EnableDepthBuffer(depth):EnableAlphaBuffer(alpha):Create()
			if InitCheck then
				self:queuecommand('AFT')
			end
		end
	}
	aft[command] = function(self)
		for i, child in pairs(self:GetChildren()) do
			child:addx(width/2):addy(height/2)
			if width == SCREEN_WIDTH and height == SCREEN_HEIGHT then
				child:basezoom(hr)
			end
		end
	end
	return aft
end

paraph.AMVQuad = function(param)
	local width = param.Width and param.Width or SCREEN_WIDTH
	local height = param.Height and param.Height or SCREEN_HEIGHT
	local color = param.Color and param.Color or {1,1,1,1}
	local uv1 = param.uv1 and param.uv1 or {0,0}
	local uv2 = param.uv2 and param.uv2 or {wratio,hratio}
	local command = param.Command and param.Command or 'InitializeCommand'
	local amv = Def.ActorMultiVertex{}
	amv[command] = function(self)
		self:SetVertices{
			{{-width/2,-height/2,0}, color, uv1},
			{{ width/2,-height/2,0}, color, {uv2[1],uv1[2]}},
			{{ width/2, height/2,0}, color, uv2},
			{{-width/2, height/2,0}, color, {uv1[1],uv2[2]}}
		}:SetDrawState{First=1,Num=-1,Mode='DrawMode_Quads'}:queuecommand('SetTexture')
	end
	return amv
end

paraph.split = function(str, ts)
	if ts == nil then return {} end
	local t = {}
	local itr = 1
	for s in string.gmatch(str, '([^'..ts..']+)') do
		t[itr] = s;
		itr = itr+1;
	end
	return t
end

paraph.addx = function(self,a) self:addx(a) end
paraph.addy = function(self,a) self:addy(a) end
paraph.addz = function(self,a) self:addz(a) end
paraph.addxy = function(self,a) self:addx(a[1]):addy(a[2]) end
paraph.addxyz = function(self,a) self:addx(a[1]):addy(a[2]):addz(a[3]) end
paraph.addrotationx = function(self,a) self:addrotationx(a) end
paraph.addrotationy = function(self,a) self:addrotationy(a) end
paraph.addrotationz = function(self,a) self:addrotationz(a) end
paraph.animate = function(self,a) self:animate(a) end
paraph.aux = function(self,a) self:aux(a) end
paraph.blend = function(self,a) self:blend(a) end
paraph.cropbottom = function(self,a) self:cropbottom(a) end
paraph.croptop = function(self,a) self:croptop(a) end
paraph.cropleft = function(self,a) self:cropleft(a) end
paraph.cropright = function(self,a) self:cropright(a) end
paraph.diffuse = function(self,a) self:diffuse(unpack(a)) end
paraph.diffusealpha = function(self,a) self:diffusealpha(a) end
paraph.fadebottom = function(self,a) self:fadebottom(a) end
paraph.fadetop = function(self,a) self:fadetop(a) end
paraph.fadeleft = function(self,a) self:fadeleft(a) end
paraph.faderight = function(self,a) self:faderight(a) end
paraph.fov = function(self,a) self:fov(a) end
paraph.queuecommand = function(self,a) self:queuecommand(a) end
paraph.playcommand = function(self,a,b) self:playcommand(a,b) end
paraph.visible = function(self,a) self:visible(a) end
paraph.x = function(self,a) self:x(a) end
paraph.y = function(self,a) self:y(a) end
paraph.z = function(self,a) self:z(a) end
paraph.xy = function(self,a) self:x(a[1]):y(a[2]) end
paraph.xyz = function(self,a) self:x(a[1]):y(a[2]):z(a[3]) end
paraph.rotationx = function(self,a) self:rotationx(a) end
paraph.rotationy = function(self,a) self:rotationy(a) end
paraph.rotationz = function(self,a) self:rotationz(a) end
paraph.rotationxy = function(self,a) self:rotationx(a[1]):rotationy(a[2]) end
paraph.rotationxyz = function(self,a) self:rotationx(a[1]):rotationy(a[2]):rotationz(a[3]) end
paraph.addrotationxyz = function(self,a) self:addrotationx(a[1]):addrotationy(a[2]):addrotationz(a[3]) end
paraph.setstate = function(self,a) self:setstate(a) end
paraph.zoomx = function(self,a) self:zoomx(a) end
paraph.zoomy = function(self,a) self:zoomy(a) end
paraph.zoomz = function(self,a) self:zoomz(a) end
paraph.zoom = function(self,a) self:zoom(a) end

return paraph