-- 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 apps=apps
local win=win


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

local wstr=require("wetgenes.string")

module("bulb.ball")

function bake(state,ball)
	local ball=ball or {}
	
--flag
	ball.ball=true

	local gl=state.gl
	local cake=state.cake
	local canvas=cake.canvas
	local flat=canvas.flat
	local game=cake.game	
	
	local beep=state.rebake("bulb.beep")
--	local input=state.rebake("bulb.input")
	local field	=state.rebake("bulb.field")
	local cells	=state.rebake("bulb.cells")
	local score	=state.rebake("bulb.score")
	local emits	=state.rebake("bulb.emits")
	local can	=state.rebake("bulb.can")

	local levels=state.rebake("bulb.levels")

	local play=state.rebake("bulb.game_play")
	

--	ball.maxball=5

	local function check_ball(id)

		if not cells.counts or cells.counts[0]==0 or cells.counts[-1] then return true end -- all are valid

		local color=can.color(id)
		if color then
			if color>0 then
				if not cells.counts[ color ] then return false end -- not valid
			end
		end
		return true
	end
	
	function ball.setup_nexts()

		local n={1}
		
		if play and play.mode=="endurance" then

			local l=4+play.level
			if l>12 then l=12 end

			for i=1,l*3 do
				n[i]=(i%l)+1
			end

-- re jiggle
			local t={}
			while #n>0 do
				t[#t+1]=table.remove(n,math.random(1,#n))
			end
			n=t

		elseif play and play.mode=="puzzle" then

			local b=levels.getbag(play.level)
			for i,v in ipairs(b) do
				n[i]=v
			end
		end
		ball.nexts=n
		ball.next=1		
	end

--[[
	function ball.random_ball()
		if play.mode=="endurance" then
			local l=4+play.level
			if l>12 then l=12 end
			return math.random(1,l)
		end
		return math.random(1,6)
	end
]]
	
	local function do_next()
		local sanity=0
		repeat
			sanity=sanity+1
			ball.id=ball.nexts[ ball.next ]		
			ball.next=ball.next+1
			if ball.next>#ball.nexts then ball.next=1 end
		until check_ball(ball.id) or sanity>128 -- OK color
	end
	
	function ball.setup_swing()
		local swing={}
		ball.swing=swing
		
		swing.cx=game.unit*8.0/2 
		swing.cy=(game.unit/2)
		swing.loop=0 -- use a master loop
		swing.loop_max=60*3
		
		function swing.set()
			ball.px=swing.px
			ball.py=swing.py
			ball.vx=swing.vx
			ball.vy=swing.vy
			ball.rz=swing.rz
		end

		function swing.update()
				local swing=ball.swing
				
				swing.loop=swing.loop+1
				if swing.loop>=swing.loop_max then swing.loop=0 end
				
				local lx=swing.px or 0
				local ly=swing.py or 0

				local f=(swing.loop/swing.loop_max)
				
				swing.rz=90+( 90*math.abs(math.abs((f*4)-1)-2))
				
				swing.px=   (1/2)*(game.unit*8.5-game.unit*3)*math.sin(math.pi*2*f)
				swing.py= -game.unit - (3/16)*(game.unit*8.5-game.unit*3)*math.cos(math.pi*4*f)
				
				swing.px=-swing.px+swing.cx
				swing.py=-swing.py+swing.cy+ball.dip

				swing.vx=(swing.px-lx)*1
				swing.vy=(swing.py-ly)*1
				
				swing.dx= swing.cx-swing.px
				swing.dy= (swing.cy-game.unit*3)-swing.py
				
				local d=math.sqrt( swing.dx*swing.dx + swing.dy*swing.dy )
				if d<=0 then d=1 end
				swing.dx=swing.dx/d
				swing.dy=swing.dy/d
		end
		
		swing.update()
		swing.update()
		
		return swing	
	end
	
	function ball.state_change()

	-- handle state changes

		if ball.state_next then
		
			if ball.state and ball.state.clean then
				ball.state.clean()
			end
			
			if type(ball.state_next)=="string" then	 -- change by name
				ball.state_name=ball.state_next
				ball.state_next=ball.states[ball.state_next]
			end

			ball.state_last=ball.state
			ball.state=ball.state_next
			ball.state_next=nil
			
			if ball.state and ball.state.setup then
				ball.state.setup()
			end
			
		end
		
	end

	ball.id=0

	ball.states={}
	ball.states.swing={
		setup=function()

						
			ball.lazer={}
			ball.ghost_cell=nil
			
			ball.states.swing.update() -- fix the first bad velocity
		end,
		update=function()
		
			if cells.state_name=="wait" then -- after cells settle
				if ball.id==0 then
					do_next()
					ball.sx=0
				end
				if ball.sx<1 then
					ball.sx=ball.sx+1/15
				end
			end
			if ball.sx>=1 then ball.sx=1 end
			
			ball.swing.set()

--			if ball.count<32 then
				ball.build_lazer(32,2)
--			end
			
			if ball.sx==1 and cells.state_name=="wait" then -- ready to drop

				local cx,cy=cells.pxy_to_cxy(ball.px,ball.py)
				local c=cells.get_cell(cx,cy)
				if cy<0 or ( c and c.id~=0 ) then -- die

					ball.state_next="die"

				elseif play.butt or play.butt_down then -- drop next ball
				
					ball.state_next="drop"

					cells.floor=cells.floor+3

				end
			end

		end,
	}
	ball.states.die={
		setup=function()
			for i,v in ipairs( ball.getdrawballs() ) do
				if v[1]>0 then
					emits.newdrop_ball(v[1],v[2],v[3],v[4],v[5],math.random(0,10)-5,-math.random(1,5))
				end
			end
			ball.gravity=(game.unit*1/256)
			beep.play("die")
			beep.stream()
		end,
		update=function()
			if emits.empty() then -- endof
				score.final()
			end
		end,
		draw=function()end,
	}
	ball.states.drop={
		setup=function()
			score.add(1)
			ball.lazer={}
			ball.ghost_cell=nil		
			ball.cell=nil
			ball.count=ball.count+1
			score.count=ball.count
			ball.dip=0--ball.dip+3 --4+math.floor(score.num/1000)
---			cells.floor=cells.floor+3
--print("dip="..ball.dip)
			ball.hits=0
--print("ballcount "..ball.count)
		end,
		update=function()
		for i=1,2 do

			local c=ball.fizix(ball)
			local land=ball.cell
			if c and c.id==0 then land=c end
			if c and ( c.id~=0 or c.cy==0 ) and land then -- lets call this a hit
			
			
				local hit=c
				local hit_id=c.id
				
				if ball.id==45 then
					cells.boss.pxpy(ball.px,ball.py)
				end

				c:trigger( can.trigger_hit(c.id) , ball )
				
				if ball.id==0 and land.id==0 then
					cells.state_next=cells.state_next or "fall" -- but must still be stable
					ball.state_next=ball.state_next or "swing"
					return
				end -- ball has been dealt with in triggers

				
				if --[[ball.hits==0 and ]] can.bounce_off(hit_id) then -- one bounce?
				
					beep.play("bounce")

					ball.px=ball.px-ball.vx
					ball.py=ball.py-ball.vy
--					ball.vx=ball.vx*-1.0
					ball.vy=ball.vy*-0.5
					ball.hits=1
					
					if ball.vx*ball.vx < 0.5 then ball.vx=math.random(-1*16,1*16)/16 end

--					ball.fizix(ball)

				else

					if c.id==0 then land=c end -- smashed what we hit

--					if c.id==0 and c.cy==0 then ball.cell=c end -- not a real hit

--					if c.id==17 then -- smash it, it is glass
--						c:shatter()
--						if ball.vy>0 then ball.vy=ball.vy*-0.5 end
--						ball.cell=c -- and we take its place
--					end

					if land then
					
						land.id=ball.id -- add it
						land.rz=ball.rz -- add it
						
--						land.fx=ball.vx
--						land.fy=ball.vy

						ball.id=0 -- stop drawing it
						land.active=cells.frame -- flag as active this game frame
						
						if land.id==45 then land.active=cells.frame-1 end -- force multi update

						cells.state_next=cells.state_next or "fall" 
						
						ball.beep("ball_land")
						
						ball.state_next=ball.state_next or "swing"
						
						for c in land:area(2) do
							c=c.master or c
							c.gx=ball.vx
							c.gy=ball.vy
						end

--[[
						emits.drop.create{
							px=ball.px,
							py=ball.py,
--							vy=4,
							life=1,
							burst=1,
							id=ball.cell.id,
						}
]]

						land:trigger( can.trigger_land(land.id) , hit )


					else
--						ball.state_next="swing"
	--print("failed to place ball")
						ball.state_next="die"
					end

				end
				break
			else	
				ball.cell=c or ball.cell -- only change if we have a new cell
			end
		end
		end
	}
	ball.states.wait={
	}

	function ball.loads()
	end
	
	function ball.setup()
		ball.loads()

		
		ball.gravity=(game.unit*1/32)

		ball.dip=0
		ball.count=0
		ball.id=0
		
		ball.setup_nexts()
		ball.sx=0
		
		ball.setup_swing()
		ball.swing.set()

		
		ball.state_next="swing"
		ball.state_change()
	end

	function ball.relaunch(id,px,py,vx,vy)
		ball.state_next="drop"
		ball.state_change()
		ball.id=id
		ball.px=px
		ball.py=py
		ball.vx=vx
		ball.vy=vy
		ball.sx=1
	end

-- grab the next ball to drop (the drops list cycles forward)
	function ball.grab()
		do_next()
		local id=ball.id
		ball.id=0
		return id
	end

	function ball.update()
		if play.pause then return end

		if not check_ball(ball.id) then -- fix the current ball even
			do_next()
		end

		ball.swing.update()

		ball.state_change()	
		if ball.state and ball.state.update then ball.state.update() end	
		
	end

	function ball.draw()
--		if play.pause then return end	
	
		if ball.state and ball.state.draw then
			ball.state.draw()
		else

			local bulbs=cake.sheets.get("bulbs")

			local s=math.floor(game.bulb_size/5)

			local a=0.5-(ball.count/64)
			a=a*ball.sx
			if ball.id>0 then
				if a>=0 then
					gl.Color(a,a,a,a)
					bulbs:batch_start()
					for i=1,#ball.lazer,2 do
						local px=ball.lazer[i]
						local py=ball.lazer[i+1]

		--				cake:blit("lazer",nil,
		--					px-(s/2) , py-(s/2),
		--					s*(ball.id-1),0,s,s )

						bulbs:draw(ball.id,px, py , ball.rz , game.bulb_size/4)
					end
					if ball.ghost_cell then
						local px=ball.ghost_cell.px
						local py=ball.ghost_cell.py-cells.float

		--				cake:blit("bulbs",nil,
		--					px-(game.bulb_size/2) , py-(game.bulb_size/2),
		--					game.bulb_size*(ball.id-1),0,game.bulb_size,game.bulb_size )

						bulbs:draw(ball.id,px, py , ball.rz , game.bulb_size )

					end
					bulbs:batch_stop()
					bulbs:batch_draw()
				end
			end

			gl.Color(1,1,1,1)		
			bulbs:batch_start()
			for i,v in ipairs( ball.getdrawballs() ) do
				if v[1] and v[1]>0 then
					bulbs:draw(v[1],v[2],v[3],v[4],v[5])
				end
			end
			bulbs:batch_stop()
			bulbs:batch_draw()
		end

		
		cake.sheets.get("well"):draw(1,160,240,0,960,960)

	end
	
	function ball.getdrawballs()
		local ret={}
		local z=(1-ball.sx)*30
		ret[#ret+1]={ball.id,ball.px +z*ball.swing.dx , ball.py +z*ball.swing.dy , ball.rz , game.bulb_size/2 + ball.sx*game.bulb_size/2}
		
		local n=ball.next
		local id
		local function get_next_id()
			local sanity=0
			repeat
				sanity=sanity+1
				id=ball.nexts[n]
				n=n+1
				if n>#ball.nexts then n=1 end
			until check_ball(id) or sanity>128
		end
		
		local zp=20
		local z=0
		if ball.sx<1 then
			z=20*(1-ball.sx)
			zp=zp*((16-(4*(1-ball.sx)))/16)
		end
		z=z+30
		for i=1,10 do
			get_next_id()
			ret[#ret+1]={id,ball.swing.px+z*ball.swing.dx, ball.swing.py+z*ball.swing.dy , ball.swing.rz , zp*(game.bulb_size/40)}
			z=z+zp
			zp=zp*((16-(4*(1)))/16)
		end
--print(ret[1][5],ret[2][5],ret[3][5],ret[4][5],ret[5][5])
		return ret
	end
	

	function ball.resets()
		ball.dangers={}
		beep.danger(0)
	end
	function ball.build_lazer(count,pace)

		local f=function()
-- adgust danger muzak
			ball.dangers=ball.dangers or {}
			if 	ball.danger_last==ball.ghost_cell then return end
			ball.dangers[#ball.dangers+1]=1.50-((#ball.lazer)/24)
			while #ball.dangers>12 do table.remove(ball.dangers,1) end

			local n=0
			for i,v in ipairs(ball.dangers) do
				if n<v then n=v end
			end
			if n>1 then n=1 end
			ball.danger=n
--print( ball.danger )
			beep.danger(ball.danger)
			ball.danger_last=ball.ghost_cell
		end

		local it={}
		it.px=ball.px
		it.py=ball.py
		it.vx=ball.vx
		it.vy=ball.vy
		
		ball.lazer={}
		ball.ghost_cell=nil
		ball.sound=false
		for i=1,count*pace do
			local c=ball.fizix(it)
			if c and c.id~=0 then ball.sound=true return f() end
			if (i)%pace==0 then
				ball.lazer[#ball.lazer+1]=it.px
				ball.lazer[#ball.lazer+1]=it.py
			end
			ball.ghost_cell=c --rember the last free cell we passed through
			if c and c.cy==0 then ball.sound=true return f() end
		end
		ball.sound=true
		ball.ghost_cell=nil -- no collision

		f()
	end

	function ball.fizix(it)
	
		it.px=it.px+(it.vx*0.5)
		it.py=it.py+(it.vy*0.5)
		it.vy=it.vy+(ball.gravity*0.5)
		
		if it.px<game.unit*1.0 then
			it.px=game.unit*1.0 + ( game.unit*1.0 - it.px )
			if it.vx<0 then
				it.vx=-it.vx
				ball.beep("ball_side")
			end
		end
		
		if it.px>game.unit*7.0 then
			it.px=(game.unit*7.0) - ( it.px - (game.unit*7.0) )
			if it.vx>0 then
				it.vx=-it.vx
				ball.beep("ball_side")
			end
		end


		local cx,cy=cells.pxy_to_cxy(it.px,it.py)
		if cy<0 then cy=0 end -- do not fall through bottom
		return cells.get_cell(cx,cy)
		
	end

	ball.sound=true
	function ball.beep(id)
		if ball.sound then
			beep.play(id)
		end
	end

	return ball
end
