effects={}
function defineEffects()
	bits 		= {}
	tweens 		= {}
	fade 		= {r=0,g=0,b=0,a=1}
	skyBacking  = true
	numBokeh  	= 18
	cumlimit 	= 100
	bloomStreak = 0.4
	bloomStrength = 0.5
	wobbleCount = 0
	cards 		= {}
	waiting 	= {}

	colors 		= {
		white={1,1,1,r=1,g=1,b=1},
		black={0,0,0},
		green={r=0.25*0.5,g=0.7*0.5,b=0.35*0.5,a=1},
		red = {r=0.8*0.5,g=0.2*0.5,b=0.2*0.5,a=1},
		white50 = {r=1,g=1,b=1,a=0.5},
		black50 = {r=0,g=0,b=0,a=0.5},
	}
	--cubeMesh = gfx.newMesh(zformat, obj.load(LINK_DIR.."models/cube.obj"), "triangles")

end

function effects.defaultCardStyle()
	return {
		 	time 				= 0
		, size 				= 32
		, center 			= false
		, over 				= false
		, box 				= false
		,	wobble 			= false
	}
end

local mat = mat4()

function newRingMesh(inner,outer,curve,arc1,arc2)
	local ring = {}
	local tris = {}
	local curve = curve or 1
	local outer = outer or inner*1.1
	local arc1,arc2  = arc1 or 0, arc2 or pi*2
	local thicc = outer-inner
	local iN = 4*curve
	local jN = curve*16
	for i=0,iN,1 do 
		local ca1,ca2 = (i/iN)*2*pi,((i+1)/iN)*2*pi
		local d1,z1 	= inner+math.sin(ca1)*thicc,math.cos(ca1)*thicc
		local d2,z2 	= inner+math.sin(ca2)*thicc,math.cos(ca2)*thicc
		for j=0,jN,1 do 
			local ra1,ra2 = (j/jN)*2*pi,((j+1)/jN)*2*pi
			if ra1>=arc1 and ra2<=arc2 then 
				local xl,yl 	= math.sin(ra1),math.cos(ra1)
				local xr,yr 	= math.sin(ra2),math.cos(ra2)
				table.insert(ring,{xl*d1,yl*d1,z1,1	,0,0, 1,1,1,1})
				table.insert(ring,{xr*d1,yr*d1,z1,1	,0,0, 1,1,1,1})
				table.insert(ring,{xl*d2,yl*d2,z2,1 ,0,0, 1,1,1,1})

				table.insert( tris, {
					vec3( xl*d1,yl*d1,z1 ),
					vec3( xr*d1,yr*d1,z1 ),
					vec3( xl*d2,yl*d2,z2 )
				})

				table.insert(ring,{xl*d2,yl*d2,z2,1	,0,0, 1,1,1,1})
				table.insert(ring,{xr*d2,yr*d2,z2,1	,0,0, 1,1,1,1})
				table.insert(ring,{xr*d1,yr*d1,z1,1 ,0,0, 1,1,1,1})

				table.insert( tris, {
					vec3( xl*d2,yl*d2,z2 ),
					vec3( xr*d2,yr*d2,z2 ),
					vec3( xr*d1,yr*d1,z1 )
				})
			end
		end
	end
	log("ring:"..#ring.." verts + "..(#ring/3).." tris")
	return gfx.newMesh(zformat,ring,"triangles"),tris
end

function newTransform(x,y,z)
	return mat4():translate(mat4(), vec3(x,y,z))
end

function newModel(mesh,transform,parent)
	return {
		mesh = mesh or cubeMesh,
		transform = transform or mat4(),
		parent = parent or room 
	}
end

gfx.stippledLine = function(x1,y1,x2,y2)
	gfx.line(x1,y1,x2,y2)
end

function resetDraws(  )
	wobbleCount = 0
	lume.clear(drawFuncs)
	
end

function gfx.brightness(c,b,a)
	local b = b or 1
	local c = c or {1,1,1,1}
	if b then 
		if c.r then 
			if a then return c.r*b, c.g*b, c.b*b,c.a or 1 else return  {r=c.r*b, g=c.g*b, b=c.b*b,a=c.a or 1} end
		else
			return c[1]*b,c[2]*b,c[3]*b,c[4] or 1
		end
	else
		return c[1],c[2],c[3],c[4] or 1
	end
end

function effects.tween(thing,properties,duration,kind,ease,func)
	local props = {}
	for p,prop in pairs(properties) do 
		table.insert(props, { key=p, startValue=thing[p] , endValue=prop })
	end
	for t,tween in lume.ripairs(tweens) do
		if tween.thing == thing then
			for p,prop in pairs(properties) do
				if multicheckOR(p,tween.props,"key") then
					tween.props[multicheckOR(p,tween.props,"key")]=nil
				end
			end
		end
	end
	table.insert(tweens,{thing=thing,props=props,startTime=time,duration=duration,kind=kind or "linear",ease=ease or 1,func=func or function()end})
end


function effects.updateTweens()
	

	for t,tween in lume.ripairs(tweens) do
		local progress = mc(0,(time-tween.startTime)/tween.duration,1)
		for p,prop in pairs(tween.props) do
			if prop.startValue then
				if tween.kind == "linear" then 
					tween.thing[prop.key] = lerp( prop.startValue, prop.endValue, progress)
				elseif tween.kind == "easeOut" then 
					tween.thing[prop.key] = lerp( prop.startValue, prop.endValue, lerp(progress,1+(progress-1)^(5) , 1))
				elseif tween.kind == "easeIn" then 
					tween.thing[prop.key] = lerp( prop.startValue, prop.endValue, lerp(progress,progress^(5), 1) )
				elseif tween.kind == "easeInOut" then 
					tween.thing[prop.key] = lerp( prop.startValue, prop.endValue, recurse(easeInOut,progress,5))
				end
			end
		end
		if progress >= 1 then 
			tween.func()
			table.remove(tweens,t)
		end
	end
end

function easeInOut(x,easeIn,easeOut)
	return x<0.5 and ((x*2)^(1+easeIn))/2 or 1-math.abs(x*2-2)^(1+easeOut)/2
end


function colorTable(color)
	return color.r and {color.r,color.g,color.b} or {r=color[1],g=color[2],b=color[3]}
end

function rgb(c)
	return {r=c.r,g=c.g,b=c.b}
end

function recurse(f,x,n)
	local r = x
	for i=1, n, 1 do
		r=f(r)
	end
	return r
end

function effects.getInverse(color)
	local backDark = mean(color)<0.6 and 0.6  or -0.4
	return contrast({r=mc(0,1-color.r+backDark,1),g=mc(0,1-color.g+backDark,1),b=mc(0,1-color.b+backDark,1)},0)
end

function effects.flashCard(color,textColor,text,time,size,center,over,box,wobble)
	local time 		= (time==0 and text:len()/15 or time) * 60
	local box 		= box and {x=box.x or (1-box.w)/2,y=box.y or (1-box.h)/2, h=box.h or 1,w=box.w or 1} or {}
	local card  	= {box=lume.merge({x=0,y=0,w=1,h=1},box),wobble=wobble}
	card.wrap 		= card.wrap or card.box.w/1.2
	card.color 		= (color~=nil) and (#color>0 and colorTable(color) or color) or room.sky.color
	local backDark 	= mean(card.color)<100 and 80  or -80
	card.textColor 	= (textColor~=nil) and (#textColor>0 and colorTable(textColor) or textColor) 
						or contrast({r=1-card.color.r+backDark,g=1-card.color.g+backDark,b=1-card.color.b+backDark},0.2)
	card.size 		= size
	card.center 	= center or {x=card.box.x+card.box.w/2,y=card.box.y+card.box.h/2}
	card.over 		= over

	if type(text)=="table" then
		local length
		if #text == 1 then 
			length  = text[1]:len()
			text  	= lume.split(text[1])

		else
			length = table.concat(text):len() 
		end
		for i,v in pairs(text) do 
			local card2 = shallowcopy(card)
			card2.text = v
			card2.time = mf(time/#text)
			--temp time proportional to length of word
			--card2.time = mf(time*(card2.text:len()/length))
			table.insert(cards,card2)
		end
	else
		card.text = text 
		card.time = time
		table.insert(cards,card)
	end
end

function effects.drawCard()
	if cards[1] then 
		local card = cards[1]
		if card.over then
			drawSetBackground()
		end
		local height 	= card.size*game.resolution
		local cb 		= effects.wobble(card.box,0.4,0.05)
		gfx.setFont 		(Aracne,height)
		gfx.setColor 	(card.color.r,card.color.g,card.color.b,1)
		gfx.rectangle 	("fill",cb.x*renderWidth,cb.y*renderHeight,cb.w*renderWidth,cb.h*renderHeight)
		gfx.setColor 	(card.textColor.r,card.textColor.g,card.textColor.b,1)
		shaders.harsh:send("tint",{card.textColor.r,card.textColor.g,card.textColor.b,1})
		gfx.setShader   (shaders.harsh)
		gfx.setBlendMode("alpha")

		if card.wobble then 
			local font = gfx.getFont()
			local wwidth = font:getWidth(card.text)*1.1
			local xoff = (card.center.x)*renderWidth-wwidth/2
			for i=1, #card.text do
				local letter =card.text:sub(i,i)
				local lwidth = font:getWidth(letter)
				gfx.printf 	(letter,effects.wobble(xoff,card.wobble[1],card.wobble[2]),effects.wobble(card.center.y*renderHeight-height/2,card.wobble[1],card.wobble[2]),lwidth,"left")
				xoff = xoff + lwidth*1.1
			end
		else
			gfx.printf 		(card.text,(card.center.x-card.wrap/2)*renderWidth,card.center.y*renderHeight-height/2,card.wrap*renderWidth,"center")
		end

		if not editingLevel then 
			card.time  		= card.time-1
		end
		if card.time<=0 then 
			table.remove(cards,1)
		end
	end
end


function effects.changeRes(z)
	--effects.fade(function ()
		game.newRes = z
	--end,0.2)
end

function effects.toggleResolution()
	if altDown then 
		if keypressed("-") and game.resolution>0.3  then
			effects.changeRes(game.resolution/2)
		end
		if keypressed("=") and game.resolution<1 then
			effects.changeRes(game.resolution*2)
		end
		if keypressed("0") then
			effects.changeRes(1)
		end
	end
end

function effects.postCamera()
	effects.drawFade()
end 

function effects.drawFade()
	gfx.setColor(fade.r,fade.g,fade.b,fade.a)
	if fade.a>0 then 
		gfx.rectangle("fill",0,0,screenWidth,screenHeight) 
	end
end

function effects.fade(func,spd)
	if not multicheckOR(fade,tweens,"thing") then
		effects.tween(fade,{a=1},spd or 0.3,"easeOut",0,function() func()  effects.tween(fade,{a=0},spd or 0.7,"easeIn") end )
	end
end

function effects.setBloom(streak,strength)
	bloomStreak 	= streak
	bloomStrength   = strength
end

function effects.wobble(wobbles,speed,dist,auto)
	local rval = wobbles
	if type(wobbles)=="table" then 
		local t =  {}
		if (speed and dist) then 
			for k,w in pairs(wobbles) do
				if type(w) == "number" then 
					t[k]=effects.wobbleNumber(w,speed,dist)
				end
			end
			rval = t
		elseif wobbles.wobbles and lume.count(wobbles.wobbles)>0 then
			for k,w in pairs(wobbles.wobbles) do
				t[k] = wobbles[k] + effects.wobbleNumber(0,w[1],w[2])
			end
			rval = t
		end
	else
		wobbles = effects.wobbleNumber(wobbles,speed,dist)
		rval = wobbles
	end
	return rval 
end

local sin = math.sin
local cos = math.cos

function effects.wobbleNumber(w,speed,dist)
	wobbleCount = wobbleCount + 1
	local x = (time + wobbleCount)*(speed or 0)
   	return w+(sin(x)+0.5*sin(2*x)+sin(x)*cos(x*pi))*(dist or 0)*0.1
end
wobbleNumber = effects.wobbleNumber


function easeTo(var,target,slow,dec)
	return round(target-var,dec or 2)==0 and target or (var + (target-var)/(slow or 4))
end

function gfx.longline(line)
	local length = 100
	if #line>length then
		for i=1,#line,length do
			local slice = lume.slice(line,i,math.min(i+length,#line))
			--if #slice%2==0 then 
				gfx.line(slice)
			--end
		end
	else
		gfx.line(line)
	end
end

function gfx.setAlpha(a)
	local r, g, b = gfx.getColor( )
	gfx.setColor(r,g,b,a)
end

