-- $Id: RingParticles.lua 3171 2008-11-06 09:06:29Z det $
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local RingParticles = {}
RingParticles.__index = RingParticles

local billShader = 0
local uPartSize  = 0

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------


function RingParticles.GetInfo()
	return {
		name      = "RingParticles",
		backup    = "", --// backup class, if this class doesn't work (old cards,ati's,etc.)
		desc      = "",

		layer     = 0, --// extreme simply z-ordering :x

		--// gfx requirement
		fbo       = false,
		shader    = true,
		rtt       = false,
		ctt       = true,
		atiseries = 1,
	}
end

RingParticles.Default = {
	lists = {},

	emitVector = {0,0,0}, -- todo
	pos        = {0,0,0}, -- start pos
	layer      = 0,

	grav       = 0,
	airdrag    = 1,
	speed      = 0,
	speedSpread= 0,
	life       = 0,
	lifeSpread = 0,
	rotSpeed   = 0,
	rotSpread  = 0,
	rotairdrag = 1,
	emitRot    = 90,
	emitRotSpread = 0,
	size       = 0,
	sizeSpread = 0,
	sizeGrowth = 0,
	colormap   = { {0, 0, 0, 0} },
	srcBlend   = GL.ONE,
	dstBlend   = GL.ONE_MINUS_SRC_ALPHA,
	texture    = '',
	count      = 0,
	repeatEffect = false,
}

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function RingParticles:InitializePartList(partList)
	partList.speed    = partList.cspeed or self.speed
	partList.vspeed   = self.grav
	partList.rotate   = self.rotSpeed
	partList.rotspeed = self.rotSpeed
	partList.size     = partList.csize or self.size
	partList.radius   = 0
	partList.altitude = 0
	partList.life     = 0
	local r,g,b,a     = GetColor(self.colormap,partList.life)
	partList.color    = {r,g,b,a}
	partList.start_pos= self.pos

	-- spread values
	if (self.speedSpread>0) then partList.speed   = partList.speed    + math.random(self.speedSpread*100)/100 end
	if (self.sizeSpread>0)  then partList.size    = partList.size     + math.random(self.sizeSpread*100)/100 end
	if (self.rotSpread>0)   then partList.rotspeed= partList.rotspeed + math.random(self.rotSpread*100)/100 end
	local rand = 0
	if (self.lifeSpread>0) then rand = math.random(self.lifeSpread) end
	partList.life_incr = 1/(self.life+rand)
end

function RingParticles:UpdatePartList(partList,n)
	partList.speed    = partList.speed  * (self.airdrag^n)
	--partList.vspeed   = (partList.vspeed + self.grav) * self.airdrag
	--partList.rotate   = (partList.rotate + partList.rotspeed) * self.rotairdrag

	local gravBoost,rotBoost = 0,0
	for i=1,n do
		gravBoost = gravBoost + self.grav*(self.airdrag^i);
		rotBoost  = rotBoost + partList.rotspeed*(self.rotairdrag^i);
	end
	partList.vspeed   = (partList.vspeed * (self.airdrag^n)) + gravBoost
	partList.rotate   = (partList.rotate * (self.rotairdrag^n)) + rotBoost

	partList.radius   = partList.radius   + n*partList.speed
	partList.altitude = partList.altitude + n*partList.vspeed
	partList.size     = partList.size     + n*self.sizeGrowth
	partList.life     = partList.life     + n*partList.life_incr
	local r,g,b,a     = GetColor(self.colormap,partList.life)
	partList.color    = {r,g,b,a}
end

function DrawParticle(size,beta)
	local one = 1+(size or 0)
	gl.BeginEnd(GL.QUADS, function()
		gl.MultiTexCoord(0,0,0)
		gl.MultiTexCoord(1,0,beta, -one,-one)
		gl.Vertex(0,0)

		gl.MultiTexCoord(0,1,0)
		gl.MultiTexCoord(1,0,beta, one,-one)
		gl.Vertex(0,0)

		gl.MultiTexCoord(0,1,1)
		gl.MultiTexCoord(1,0,beta, one,one)
		gl.Vertex(0,0)

		gl.MultiTexCoord(0,0,1)
		gl.MultiTexCoord(1,0,beta, -one,one)
		gl.Vertex(0,0)
	end)
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function RingParticles:BeginDraw()
	gl.UseShader(billShader)
end

function RingParticles:EndDraw()
	gl.Texture(false)
	gl.Blending(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA)
	gl.UseShader(0)
end

function RingParticles:BeginShadow()
	gl.DepthMask(true)
	gl.UseShader(shadowShader)
	local xmid, ymid, p17, p18 = gl.GetShadowMapParams()
	gl.Uniform(gl.GetUniformLocation(shadowShader, 'xmid'), xmid)
	gl.Uniform(gl.GetUniformLocation(shadowShader, 'ymid'), ymid)
	gl.Uniform(gl.GetUniformLocation(shadowShader, 'p17'),  p17)
	gl.Uniform(gl.GetUniformLocation(shadowShader, 'p18'),  p18)
end

function RingParticles:EndShadow()
	gl.Texture(false)
	gl.Blending(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA)
	gl.UseShader(0)
	gl.DepthMask(false)
end

function RingParticles:Draw()
	--if self.genmipmap then
	--  gl.GenerateMipmap(self.texture)
	--  self.genmipmap = false
	--end

	gl.Texture(self.texture)
	gl.Blending(self.srcBlend,self.dstBlend)

	gl.PushMatrix()
	gl.Translate(self.pos[1],self.pos[2],self.pos[3])
	gl.Rotate(self.emitRot,self.emitVector[1],self.emitVector[2],self.emitVector[3])

	for _,partList in ipairs(self.lists) do
		if (partList.life < 1)and
			Spring.IsSphereInView(partList.start_pos[1],partList.start_pos[2],partList.start_pos[3],partList.radius+partList.size)
		then
			gl.Uniform(uPartSize, partList.size)

			gl.Color(partList.color)

			gl.PushMatrix()
				gl.Translate(0,partList.altitude,0)
				gl.Rotate(partList.rotate,0,1,0)
				gl.Scale(partList.radius,1,partList.radius)
				gl.CallList(partList.dlist)
			gl.PopMatrix()
		end
	end

	gl.PopMatrix()
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function RingParticles:Initialize()
	billShader = gl.CreateShader({
		vertex = [[
			uniform float size;

			varying vec2 texCoord;

	void main()
	{
						gl_Position     = gl_ModelViewMatrix * gl_Vertex;
						gl_Position.xy += gl_MultiTexCoord1.zw * size;
						gl_Position     = gl_ProjectionMatrix  * gl_Position;

						texCoord  = gl_MultiTexCoord0.st;
						gl_FrontColor   = gl_Color;
	}
		]],
		fragment = [[
			uniform sampler2D tex0;

			varying vec2 texCoord;

			void main(void)
			{
				gl_FragColor    = texture2D(tex0, texCoord)*gl_Color;
				//gl_FragDepth    = gl_FragCoord.z - (gl_FragColor.a/500);
			}
		]],
		uniformInt = {
			tex0 = 0,
		},
		uniform = {
			size  = 10,
		},
	})

	if (billShader==nil) then
		print(PRIO_MAJOR,"LUPS: RingParticles: Critical Shader Error: " ..gl.GetShaderLog())
		return false
	end

	-- get uniform pointers
	uPartSize  = gl.GetUniformLocation(billShader, 'size')
end

function RingParticles:Finalize()
	gl.DeleteShader(billShader)
end
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function RingParticles:Update(n)
	for _,partList in ipairs(self.lists) do
		if (partList.life<1) then
			self:UpdatePartList(partList,n)
		end
	end
end

-- used if repeatEffect=true;
function RingParticles:ReInitialize()
	for _,partList in ipairs(self.lists) do
		self:InitializePartList(partList)
	end
	self.dieGameFrame = self.dieGameFrame + self.life + self.lifeSpread
end

function RingParticles:CreateParticle()
	local listsCount = self.lifeSpread/5
	for i=0,listsCount do
		local newPartList = {}

		self:InitializePartList(newPartList)

		--needed for repeat mode
		newPartList.csize  = newPartList.size
		newPartList.cspeed = newPartList.speed

		newPartList.dlist = gl.CreateList(function(count)
			for y = 0, count do
				--gl.Rotate(360/count,0,1,0)
				local beta = math.random(360)
				gl.Rotate(beta,0,1,0)
				gl.Translate(0,0,1)
				DrawParticle(math.random(self.sizeSpread*100)/100,beta)
				gl.Translate(0,0,-1)
			end
		end, self.count/listsCount)

		table.insert(self.lists,newPartList)
	end

	self.firstGameFrame = Spring.GetGameFrame()
	self.dieGameFrame   = self.firstGameFrame + self.life + self.lifeSpread
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function RingParticles.Create(Options)
	local newObject = MergeTable(Options, RingParticles.Default)
	setmetatable(newObject,RingParticles)  -- make handle lookup
	return newObject
end

function RingParticles:Destroy()
	for _,partList in ipairs(self.lists) do
		gl.DeleteList(partList.dlist)
	end
	gl.DeleteTexture(self.texture)
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

return RingParticles
