local ProjectileRibbon = {}
ProjectileRibbon.__index = ProjectileRibbon

local ProjectileRibbonShader
local widthLoc, quadsLoc
local oldPosUniform = {}

local DLists = {}

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

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

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

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

ProjectileRibbon.Default = {
	layer = 1,
	life     = math.huge,
	projectile = 0,
	width    = 1,
	size     = 24, --//max 32
	color    = {0.9,0.9,1,1},
	searchradius = 64,
	saturate = false,

	worldspace = true,
	repeatEffect = true,
	dieGameFrame = math.huge
}

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
local glUniform    = gl.Uniform
local glUniformInt = gl.UniformInt
local glUseShader  = gl.UseShader
local glColor      = gl.Color
local glCallList   = gl.CallList
local glTexture    = gl.Texture
local glBlending   = gl.Blending
local GL_ONE = GL.ONE
local GL_SRC_ALPHA = GL.SRC_ALPHA
local GL_ONE_MINUS_SRC_ALPHA = GL.ONE_MINUS_SRC_ALPHA
local GL_SRC_ALPHA_SATURATE = GL.SRC_ALPHA_SATURATE

function GetProjectilePos(projectile)
	local GetProjectilePosition = Spring.GetProjectilePosition
	local x,y,z = GetProjectilePosition(projectile)
	return x,y,z
end

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

function ProjectileRibbon:BeginDraw()
	glUseShader(ProjectileRibbonShader)
	glTexture(0,":c:bitmaps/GPL/Lups/jet.tga")
end


function ProjectileRibbon:EndDraw()
	glUseShader(0)
	glBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
	glTexture(0,false)
	glColor(1,1,1,1)
end


function ProjectileRibbon:Draw()
	local quads0 = self.quads0

	glUniform(widthLoc, self.width )
	glUniformInt(quadsLoc, quads0 )

  --// insert old pos
	local j = ((self.posIdx==self.size) and 1) or (self.posIdx+1)
	for i=1,quads0 do
		local dir = self.oldPos[j]
		j = ((j==self.size) and 1) or (j+1)
		glUniform( oldPosUniform[i] , dir[1], dir[2], dir[3] )
	end

  --// insert interpolated current projectile pos
	local x,y,z = GetProjectilePos(self.projectile)

	glUniform( oldPosUniform[quads0+1] , x,y,z )

  --// define color and add speed blending (don't show ribbon for slow/landing units!)

	if self.saturate == true then
		glBlending(GL_ONE,GL_SRC_ALPHA_SATURATE)
	else
		glBlending(GL_SRC_ALPHA,GL_ONE)
	end

	glColor(self.color)

	glCallList(DLists[quads0])
end

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

function ProjectileRibbon.Initialize()
  ProjectileRibbonShader = gl.CreateShader({
    vertex = [[
      uniform float width;
      uniform int   quads;
      uniform vec3 oldPos[32];

varying vec2 texCoordFoo;

      void main()
      {
         vec3 updir,right;
         vec3 vertex = oldPos[int(gl_MultiTexCoord0.w)];
         gl_Position = gl_ModelViewMatrix * vec4(vertex,1.0);

         vec3 vertex2,pos2;
         if (int(gl_MultiTexCoord0.w) == quads) {
           vertex2 = oldPos[int(gl_MultiTexCoord0.w)-1];
           pos2   = (gl_ModelViewMatrix * vec4(vertex2,1.0)).xyz;
           updir = gl_Position.xyz - pos2.xyz;
         }else{
           vertex2 = oldPos[int(gl_MultiTexCoord0.w)+1];
           pos2   = (gl_ModelViewMatrix * vec4(vertex2,1.0)).xyz;
           updir = pos2.xyz - gl_Position.xyz;
         }

         right = normalize( cross(updir,gl_Position.xyz) );

         gl_Position.xyz += right * gl_MultiTexCoord0.x * width;
         gl_Position    = gl_ProjectionMatrix * gl_Position;

         texCoordFoo.st = gl_MultiTexCoord0.pt;
         gl_FrontColor = gl_Color;
      }
    ]],
    fragment = [[
varying vec2 texCoordFoo;
      uniform sampler2D ribbonTex;

      void main()
      {
         gl_FragColor = texture2D(ribbonTex, texCoordFoo.st )*gl_Color;
      }
    ]],
    uniformInt={
      ribbonTex = 0,
    },
  })

	if (ProjectileRibbonShader == nil) then
		print(PRIO_MAJOR,"LUPS->ProjectileRibbon: critical shader error: "..gl.GetShaderLog())
		return false
	end

	widthLoc = gl.GetUniformLocation(ProjectileRibbonShader, 'width')
	quadsLoc = gl.GetUniformLocation(ProjectileRibbonShader, 'quads')

	for i=1,32 do
		oldPosUniform[i] = gl.GetUniformLocation(ProjectileRibbonShader,"oldPos["..(i-1).."]")
		end
	end

function ProjectileRibbon.Finalize()
	if (gl.DeleteShader) then
		gl.DeleteShader(ProjectileRibbonShader)
	end

	while (next(DLists)) do
		local i,v = next(DLists)
		DLists[i] = nil
		gl.DeleteList(v)
	end
end

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

local function CreateDList(quads0)
	for i=0,quads0-1 do
		local tex_t      = i/quads0
		local tex_t_next = (i+1)/quads0

		gl.TexCoord(-1,tex_t_next,0,i+1)
		gl.Vertex(0,0,0)
		gl.TexCoord(1,tex_t_next,1,i+1)
		gl.Vertex(0,0,0)
		gl.TexCoord(1,tex_t,1,i)
		gl.Vertex(0,0,0)
		gl.TexCoord(-1,tex_t,0,i)
		gl.Vertex(0,0,0)
	end
end


function ProjectileRibbon:Update(n)
	local GetProjectilePosition = Spring.GetProjectilePosition
	local x,y,z = GetProjectilePosition(self.projectile)
	self.isvalid = x

	if self.isvalid ~= nil then
		self.posIdx = (self.posIdx % self.size)+1
		self.oldPos[self.posIdx] = {x,y,z}
	end
end


-- used if repeatEffect=true;
function ProjectileRibbon:ReInitialize()
	self.dieGameFrame = self.dieGameFrame + self.life
end


function ProjectileRibbon:CreateParticle()
	if (self.size>32) then self.size=32
	elseif (self.size<5) then self.size=5 end

	self.posIdx = 1
	self.quads0 = self.size-1
	self.blendfactor = 1

	local GetProjectilePosition = Spring.GetProjectilePosition
	local x,y,z = GetProjectilePosition(self.projectile)
	local curpos = {x,y,z}

	self.oldPos = {}
	for i=1,self.size do
		self.oldPos[i] = curpos
	end

	if (not DLists[self.quads0]) then
		DLists[self.quads0] = gl.CreateList(gl.BeginEnd,GL.QUADS,CreateDList,self.quads0)
	end

	self.startGameFrame = thisGameFrame
	self.dieGameFrame   = self.startGameFrame + self.life
end


function ProjectileRibbon:Visible()
	local pos = self.oldPos[self.posIdx]
	local spIsSphereInView = Spring.IsSphereInView
	return (self.isvalid) and (spIsSphereInView(pos[1],pos[2],pos[3], self.searchradius))
end


function ProjectileRibbon:Valid()
	return self.isvalid
end

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

local MergeTable   = MergeTable
local setmetatable = setmetatable

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

function ProjectileRibbon:Destroy()
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
return ProjectileRibbon