-- copy all globals into locals, some locals are prefixed with a G to reduce name clashes
local coroutine,package,string,table,math,io,os,debug,assert,dofile,error,_G,getfenv,getmetatable,ipairs,Gload,loadfile,loadstring,next,pairs,pcall,print,rawequal,rawget,rawset,select,setfenv,setmetatable,tonumber,tostring,type,unpack,_VERSION,xpcall,module,require=coroutine,package,string,table,math,io,os,debug,assert,dofile,error,_G,getfenv,getmetatable,ipairs,load,loadfile,loadstring,next,pairs,pcall,print,rawequal,rawget,rawset,select,setfenv,setmetatable,tonumber,tostring,type,unpack,_VERSION,xpcall,module,require

local function print(...) _G.print(...) end

local wstr=require("wetgenes.string")


local function mrandom(mn,mx,mp)
	return math.random(mn/mp,mx/mp)*mp
end


local M={ modname=(...) } ; package.loaded[M.modname]=M

function M.bake(oven,emits)
	local emits=emits or {}
	local parts={}
	
	local cake=oven.cake
	local game=cake.game
	local gl=oven.gl

	local canvas=cake.canvas
	local flat=canvas.flat

	local field	=oven.rebake("bulb.field")
	local cells	=oven.rebake("bulb.cells")
	
-- BASE
	do
		local f={}		emits.base=f

		function f.create(e)

			e.destroy=e.f.destroy or emits.base.destroy
			e.update=e.f.update or emits.base.update
			e.draw=e.f.draw or emits.base.draw

			e.pcreate=e.f.pcreate or emits.base.pcreate
			e.pdestroy=e.f.pdestroy or emits.base.pdestroy
			e.pupdate=e.f.pupdate or emits.base.pupdate
			e.pdraw=e.f.pdraw or emits.base.pdraw

			emits.tab[ #emits.tab+1 ]=e

			e.tab={}

			e.pr=e.pr or 0
			e.px=e.px or 0 -- position
			e.py=e.py or 0

			e.vr=e.vr or 0
			e.vx=e.vx or 0 -- velocty
			e.vy=e.vy or 0

			e.state="live"
			
			e.time		=e.time			or 0
			e.life		=e.life			or 1
			e.burst		=e.burst		or 1
			
			return e
		end
		function f.destroy(e)
			e.state="dead"
		end
		function f.update(e)
		
			e.time=e.time+1
		
			e.pr=e.pr+e.vr
			e.px=e.px+e.vx
			e.py=e.py+e.vy
			
			for i=#e.tab,1,-1 do
				local p=e.tab[i]
				if p.state~="dead" then e.pupdate(p)	end
				if p.state=="dead" then table.remove(e.tab,i) end
			end
			

			if e.time<=e.life then -- still alive so spit stuff out
				for i=1,e.burst do
					e.pcreate(e)
				end
			else 						-- we are dormant
				if not e.tab[1] then 	-- and dead
					e.destroy(e)
				end
			end
			
		end
		function f.draw(e)
			for i=#e.tab,1,-1 do
				local p=e.tab[i]
				e.pdraw(p)
			end
		end

		function f.pcreate(e,p)
			p.e=e
			
			e.tab[ #e.tab+1 ]=p

			p.pr=p.pr or e.pr or 0
			p.px=p.px or e.px or 0 -- position
			p.py=p.py or e.py or 0

			p.vr=p.vr or e.vr or 0
			p.vx=p.vx or e.vx or 0 -- velocity
			p.vy=p.vy or e.vy or 0

			p.state="live"
			
			p.time = p.time or e.time or 0
			p.life = p.life or e.life or 1
			
			return p
		end
		function f.pdestroy(p)
			p.state="dead"
		end
		function f.pupdate(p)
			p.time=p.time+1
			p.pr=p.pr+p.vr
			p.px=p.px+p.vx
			p.py=p.py+p.vy
			if p.time>p.life then -- we are dead
				p.e.pdestroy(p)
			end
		end
		function f.pdraw(p)
		end
	end

-- BLOB
	do
		local f={}		emits.blob=f
		function f.create(t)
			t.f=f
			local e=emits.base.create(t)
			e.id=t.id or 1
			return e
		end
		function f.pcreate(e,t)

			if not t then
				t={
					px=e.px,
					py=e.py,
					vr=mrandom(-4,4,1/64),
					vx=mrandom(-4,4,1/64),
					vy=mrandom(-12,-4,1/64),
					life=128,
					id=e.id,
				}
			end

			local p=emits.base.pcreate(e,t)
			p.id=t.id or 18
			p.a=1
			return p
		end
		function f.pupdate(p)
			emits.base.pupdate(p)
			p.a=1 - (p.time / p.life) -- p.a*(31/32)
			p.a=0.5*p.a*p.a
			p.vy=p.vy+(4/8)
		end
		function f.pdraw(p)
			emits.base.pdraw(p)
			local s=64+p.time--((1-p.a)*64)
--			gl.Color(p.a,p.a,p.a,p.a)
			local bulbs=cake.sheets.get("bulbs")
			bulbs:draw(p.id,p.px,p.py,p.pr,s)
		end
		function f.draw(e)
			local bulbs=cake.sheets.get("bulbs")
			bulbs:batch_start()
			for i=#e.tab,1,-1 do
				local p=e.tab[i]
				e.pdraw(p)
			end
			bulbs:batch_stop()
			local p=e.tab[1]
			if p then gl.Color(p.a,p.a,p.a,p.a) end
			bulbs:batch_draw()
		end
	end
	
-- SPURT
	do
		local f={}		emits.spurt=f
		function f.create(t)
			t.f=f
			local e=emits.base.create(t)
			return e
		end
		function f.pcreate(e,t)

			if not t then
				t={
					px=e.px,
					py=e.py,
					vr=mrandom(-4,4,1/64),
					vx=mrandom(-3,3,1/64),
					vy=mrandom(-16,-8,1/64),
					life=128,
					id=e.id,
				}
			end

			local p=emits.base.pcreate(e,t)
			p.points={}
			p.a=1
			p.r=math.random(64,255)/255
			p.g=math.random(64,255)/255
			p.b=math.random(64,255)/255
			return p
		end
		function f.pupdate(p)
			emits.base.pupdate(p)

			local d={}
			d.x1=p.px+p.vy*0.25
			d.y1=p.py-p.vx*0.25
			d.x2=p.px-p.vy*0.25
			d.y2=p.py+p.vx*0.25
			p.points[ #p.points+1 ]=d
			
			if #p.points>16 then table.remove(p.points,1) end

			p.a=p.a*(31/32)
			p.vy=p.vy+(4/8)

		end
		function f.pdraw(p)
			if #p.points<2 then return end
			
			emits.base.pdraw(p)
			local t={}
			
			local a=p.a
			local l=#p.points
			for i=l,1,-1 do local d=p.points[i]
			
				t[#t+1]=d.x1
				t[#t+1]=d.y1
				t[#t+1]=0

				t[#t+1]=a
				t[#t+1]=a
				t[#t+1]=a
				t[#t+1]=a

				t[#t+1]=d.x2
				t[#t+1]=d.y2
				t[#t+1]=0

				t[#t+1]=a
				t[#t+1]=a
				t[#t+1]=a
				t[#t+1]=a
			
				a=p.a*i/16
			end
	
			gl.Color(p.r,p.g,p.b,p.a)
			flat.tristrip("xyzrgba",t)
--[[
			local s=p.time--((1-p.a)*64)
			gl.Color(p.a,p.a,p.a,p.a)
			local bulbs=cake.sheets.get("bulbs")
			bulbs:draw(p.id,p.px,p.py,p.pr,s,s)
]]
		end
	end	
	

-- BOOM
	do
		local function draw_circ(px,py,r1,r2,s)
			local t={}

			local x,y
			for i=0,s do
			
				x=-math.sin(i*2*math.pi/s)
				y=math.cos(i*2*math.pi/s)

				t[#t+1]=px + r1 * x
				t[#t+1]=py + r1 * y
				t[#t+1]=0

				t[#t+1]=1
				t[#t+1]=0
				t[#t+1]=0
				t[#t+1]=0

				t[#t+1]=px + r2 * x
				t[#t+1]=py + r2 * y
				t[#t+1]=0

				t[#t+1]=1
				t[#t+1]=1
				t[#t+1]=0
				t[#t+1]=0
			
			end

			flat.tristrip("xyzrgba",t)
			
		end	
		local f={}		emits.boom=f
		function f.create(t)
			t.f=f
			local e=emits.base.create(t)
			return e
		end
		function f.pcreate(e,t)

			if not t then
				t={
					px=e.px,
					py=e.py,
					vr=mrandom(-4,4,1/64),
					life=128,
					id=e.id,
					r1=mrandom(32,64,1/64),
					r2=mrandom(32,64,1/64),
				}
			end

			local p=emits.base.pcreate(e,t)
			p.a=1
			p.r1=t.r1 or 64
			p.r2=t.r2 or 64
			return p
		end
		function f.pupdate(p)
			emits.base.pupdate(p)

			p.a=p.a*(30/32)

		end
		function f.pdraw(p)
			
			emits.base.pdraw(p)

			gl.Color(p.a,p.a,p.a,0)

			gl.PushMatrix()
			gl.Translate(p.px,p.py,0)
			gl.Rotate(p.pr,0,0,1)
			
			draw_circ(0,0,p.r1+p.time*16,p.r1+p.time*16+p.r2,12)

			if p.time<p.r1 then
				draw_circ(0,0,0,p.r1-p.time,12)
			end

			gl.PopMatrix()
			
		end
	end	
	
-- SPONGE
	do
		local f={}		emits.sponge=f
		function f.create(t)
			t.f=f
			local e=emits.base.create(t)
			e.id=t.id or 1
			return e
		end
		function f.pcreate(e,t)

			if not t then
				t={
					px=e.px,
					py=e.py,
					vr=mrandom(-4,4,1/64),
					vx=mrandom(-2,2,1/64),
					vy=mrandom(-8,-6,1/64),
					life=1280,
					id=e.id,
				}
			end

			local p=emits.base.pcreate(e,t)
			p.id=t.id or 24
			p.a=1
			return p
		end
		function f.pupdate(p)
			emits.base.pupdate(p)
--			p.a=p.a*(31/32)
			if p.px < field.px+32 and p.vx<0 then p.vx=-p.vx end
			if p.px > field.px+field.hx-32 and p.vx>0 then p.vx=-p.vx end
			local h=field.py+field.hy-cells.float
			if p.py < h then
				p.vy=p.vy+(1/4)
			else
				p.vy=p.vy-((p.py-h)*1/64)
				cells.floor=cells.floor-1
			end
			if cells.floor<=0 then
				cells.floor=0
				p.a=p.a-(1/64)
				if p.a<0 then
					p.e.pdestroy(p)
				end
			end

--			p.vx=p.vx*(252/256)
			p.vy=p.vy*(254/256)
		end
		function f.pdraw(p)
			emits.base.pdraw(p)
			local s=p.time--((1-p.a)*64)
			gl.Color(p.a,p.a,p.a,p.a)
			local bulbs=cake.sheets.get("bulbs")
			bulbs:draw(p.id,p.px,p.py,p.pr,80,80)
		end
	end
		
-- DROP
	do
		local f={}		emits.drop=f
		function f.create(t)
			t.f=f
			local e=emits.base.create(t)
			e.id=t.id or 1
			return e
		end
		function f.pcreate(e,t)

			if not t then
				t={
					vr=e.vr or mrandom(-4,4,1/64),
					vx=e.vx or mrandom(-1,1,1/64),
					vy=e.vy or mrandom(-1,1,1/64),
					life=128,
				}
			end

			local p=emits.base.pcreate(e,t)
				p.id=e.id or 18
				p.a=1
				p.sz=e.sz
			return p
		end
		function f.pupdate(p)
			emits.base.pupdate(p)
--			p.a=p.a*(31/32)
			p.vy=p.vy+(4/8)
		end
		function f.pdraw(p)
			emits.base.pdraw(p)
			gl.Color(p.a,p.a,p.a,p.a)
			local bulbs=cake.sheets.get("bulbs")
			bulbs:draw(p.id,p.px,p.py,p.pr,p.sz)
		end
	end
-- FIRE
	do
		local function draw_bar(py,h)
			local w=1024
			local t={}
			local n

			n=#t
			t[n+1],t[n+2],t[n+3]			=	-w	, py-h*1.25,	0
			t[n+4],t[n+5],t[n+6],t[n+7]		=	0,0,0,0
			n=#t
			t[n+1],t[n+2],t[n+3]			=	w	, py-h*1.25,	0
			t[n+4],t[n+5],t[n+6],t[n+7]		=	0,0,0,0

			n=#t
			t[n+1],t[n+2],t[n+3]			=	-w	, py-h,	0
			t[n+4],t[n+5],t[n+6],t[n+7]		=	1,1,1,1
			n=#t
			t[n+1],t[n+2],t[n+3]			=	w	, py-h,	0
			t[n+4],t[n+5],t[n+6],t[n+7]		=	1,1,1,1

			n=#t
			t[n+1],t[n+2],t[n+3]			=	-w	, py,	0
			t[n+4],t[n+5],t[n+6],t[n+7]		=	0,0,0,0
			n=#t
			t[n+1],t[n+2],t[n+3]			=	w	, py,	0
			t[n+4],t[n+5],t[n+6],t[n+7]		=	0,0,0,0

			flat.tristrip("xyzrgba",t)
			
		end	
		local f={}		emits.fire=f
		function f.create(t)
			t.f=f
			local e=emits.base.create(t)
			return e
		end
		function f.pcreate(e,t)

			if not t then
				local hy=mrandom(16,256,1/64)
				t={
					px=e.px,
					py=e.py+(hy/2),
					life=128,
					id=e.id,
					hy=hy,
					vy=mrandom(-16,-8,1/64),
				}
			end

			local p=emits.base.pcreate(e,t)
			p.hy=t.hy or 64

			p.a=1
			p.r=mrandom( 0.75 , 1    ,1/64)
			p.g=mrandom( 0.25 , 0.5  ,1/64)
			p.b=mrandom( 0.0 , 0.25 ,1/64)

			return p
		end
		function f.pupdate(p)
			emits.base.pupdate(p)

			p.a=1-(p.time/p.life)
			p.a=p.a*p.a
			
			p.vy=p.vy-1
		end
		function f.pdraw(p)
			
			emits.base.pdraw(p)

			gl.Color(p.a*p.r,p.a*p.g,p.a*p.b,0)
			draw_bar(p.py,p.hy)
		end
	end	
	
	function emits.loads()
	end
	
	function emits.setup()
		
		emits.tab={}
		
	end
	
	function emits.update()
	
		for i=#emits.tab,1,-1 do local it=emits.tab[i]
			it:update()
			if it.state=="dead" then table.remove(emits.tab,i) end
		end
		
	end

	function emits.draw()

		cake.sheets.get("menuwheel"):draw(1,0,0,(game.frame/4)%360,128,128)
		cake.sheets.get("shopwheel"):draw(1,320,0,(game.frame/3.5)%360,128,128)

		if oven.opts.smell=="gamestick" then
			cake.sheets.get("l1"):draw(1,  0,0+16+4,0,32+8,32+8)
			cake.sheets.get("r1"):draw(1,320,0+16+4,0,32+8,32+8)
		end

		for i=#emits.tab,1,-1 do local it=emits.tab[i]
			it:draw()
		end

		gl.Color(1,1,1,1)	
	end
	
	function emits.newdrop_cell(cell)
		local s=1
		if cell.master then
			if cell.master~=cell then return end
			s=cell.blobs.size
		end
		return emits.newdrop_ball(cell.id,cell:getpx(),cell:getpy(),cell.rz,game.bulb_size*s,0,-5)
	end
	
	function emits.newdrop_ball(id,px,py,pr,sz,vx,vy)
		return emits.drop.create{
			px=px,
			py=py,
			pr=pr,
			sz=sz or game.bulb_size,
			vx=vx,
			vy=vy,
			life=1,
			burst=1,
			id=id,
		}
	end	

	function emits.empty()
		if #emits.tab==0 then return true end
	end
	
	return emits
end
