-- $Id: UnitJitter.lua 4454 2009-04-20 11:45:38Z jk $
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local UnitJitter = {}
UnitJitter.__index = UnitJitter

local warpShader, warpShader2
local tex
local timerUniform
local lastCullFace = GL.FRONT

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

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

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

    --// gfx requirement
    fbo       = true,
    shader    = true,
    distortion= true,
    intel     = 0,
  }
end

UnitJitter.Default = {
  layer      = 15,
  worldspace = true,

  inverse    = false,
  life       = math.huge,
  unit       = -1,
  unitDefID  = 0,
  team       = -1,
  allyTeam   = -1,

  repeatEffect = false,
  dieGameFrame = math.huge
}


local noiseTexture = 'bitmaps/GPL/Lups/perlin_noise.jpg'

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

local GL_BACK = GL.BACK
local GL_FRONT = GL.FRONT
local glCulling = gl.Culling
local glUnit = gl.Unit
local glUniform = gl.Uniform
local glColor = gl.Color
local glMultiTexCoord = gl.MultiTexCoord

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

function UnitJitter:BeginDrawDistortion()
  gl.UseShader(warpShader)
  gl.Uniform(timerUniform,  Spring.GetGameSeconds()*0.1 - 0.5 )
  --gl.PolygonOffset(0,-1)
  lastCullFace = GL_FRONT
  gl.Culling(GL_FRONT)

  gl.Texture(0,noiseTexture)

  gl.PushAttrib(GL.DEPTH_BUFFER_BIT)
  gl.DepthTest(true)
  gl.DepthMask(true)
end

function UnitJitter:EndDrawDistortion()
  gl.PopAttrib()
  gl.UseShader(0)
  gl.Texture(0,false)
  gl.PolygonOffset(false)
  gl.Culling(GL_BACK)
  gl.Culling(false)
end

function UnitJitter:DrawDistortion()
  if (self.inverse) then
    glMultiTexCoord(2, (thisGameFrame-self.firstGameFrame)/self.life )
  else
    glMultiTexCoord(2, 1-((thisGameFrame-self.firstGameFrame)/self.life) )
  end

  if (self.isS3o) then
    if (lastCullFace~=GL_BACK) then
      lastCullFace = GL_BACK
      glCulling(GL_BACK)
    end
  else
    if (lastCullFace~=GL_FRONT) then
      lastCullFace = GL_FRONT
      glCulling(GL_FRONT)
    end
  end

  glUnit(self.unit,true,-1)
end


function UnitJitter:BeginDraw()
  gl.UseShader(warpShader2)
  gl.Blending(GL.ONE,GL.ONE)
  lastCullFace = GL_FRONT
  gl.Culling(GL_FRONT)
  gl.Texture(0,noiseTexture)
  gl.PushAttrib(GL.DEPTH_BUFFER_BIT)
  gl.DepthTest(true)
  gl.DepthMask(true)
end

function UnitJitter:EndDraw()
  gl.PopAttrib()
  gl.UseShader(0)
  gl.Texture(0,false)
  gl.Blending(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA)
  gl.PolygonOffset(false)
  gl.Culling(GL_BACK)
  gl.Culling(false)
  gl.Color(1,1,1,1)
end

function UnitJitter:Draw()
  if (self.inverse) then
    glMultiTexCoord(2, (thisGameFrame-self.firstGameFrame)/self.life )
  else
    glMultiTexCoord(2, 1-((thisGameFrame-self.firstGameFrame)/self.life) )
  end

  if (self.isS3o) then
    if (lastCullFace~=GL_BACK) then
      lastCullFace = GL_BACK
      glCulling(GL_BACK)
    end
  else
    if (lastCullFace~=GL_FRONT) then
      lastCullFace = GL_FRONT
      glCulling(GL_FRONT)
    end
  end

  glColor(self.teamColor)
  glUnit(self.unit,true,-1)
end

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

function UnitJitter.Initialize()
  warpShader = gl.CreateShader({
    vertex = [[
      uniform float timer;

      varying vec3 texCoord;

      const vec4 ObjectPlaneS = vec4(0.005, 0.005, 0.000,  0.0);
      const vec4 ObjectPlaneT = vec4(0.000, 0.005, 0.005,  0.0);

	void main()
	{
            gl_Position = ftransform();
            texCoord.s = dot( gl_Vertex, ObjectPlaneS ) + timer; 
            texCoord.t = dot( gl_Vertex, ObjectPlaneT ) + timer;
            texCoord.z = gl_MultiTexCoord2.x; //life
            texCoord.z *= abs( dot(normalize(gl_NormalMatrix * gl_Normal), normalize(vec3(gl_ModelViewMatrix * gl_Vertex))) );
            texCoord.z *= 0.015;
	}
    ]],
    fragment = [[
      uniform sampler2D noiseMap;

      varying vec3 texCoord;

      #define life texCoord.z

      void main(void)
      {
          vec2 noiseVec;
          noiseVec = texture2D(noiseMap, texCoord.st).rg;
          noiseVec = (noiseVec - 0.50) * life;

          gl_FragColor = vec4(noiseVec,0.0,gl_FragCoord.z);
      }
    ]],
    uniformInt = {
      noiseMap = 0,
    },
    uniformFloat = {
      timer = 0,
    }
  })

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

  timerUniform  = gl.GetUniformLocation(warpShader, 'timer')

  warpShader2 = gl.CreateShader({
    vertex = [[
      void main()
      {
          gl_Position = ftransform();
          float opac = 1.0-abs( dot(normalize(gl_NormalMatrix * gl_Normal), normalize(vec3(gl_ModelViewMatrix * gl_Vertex))) );
          float life = gl_MultiTexCoord2.x; //life

          gl_FrontColor = mix( gl_Color * (opac+0.15),
                              vec4( opac*opac ),
                              opac * 0.5) * life * 0.75;
      }
    ]],
    fragment = [[
      void main(void)
      {
          gl_FragColor  = gl_Color;
      }
    ]],
    uniformFloat = {
      life  = 1,
    }
  })

  if (warpShader2 == nil) then
    print(PRIO_MAJOR,"LUPS->UnitJitter: critical shader2 error: "..gl.GetShaderLog())
    return false
  end
end

function UnitJitter.Finalize()
  if (gl.DeleteShader) then
    gl.DeleteShader(warpShader)
  end
end

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

local spGetTeamColor = Spring.GetTeamColor

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

function UnitJitter:CreateParticle()
  local name = (UnitDefs[self.unitDefID].model and UnitDefs[self.unitDefID].model.name) or UnitDefs[self.unitDefID].modelname
  self.isS3o = ((name:lower():find("s3o") or name:lower():find("obj") or name:lower():find("dae")) and true)
  self.teamColor = {spGetTeamColor(self.team)}
  self.firstGameFrame = thisGameFrame
  self.dieGameFrame   = self.firstGameFrame + self.life
end

function UnitJitter:Visible()
  if LocalAllyTeamID == -2 -- spec full view
  or LocalAllyTeamID == self.allyTeam then
    return Spring.IsUnitVisible(self.unit, 0, true) -- Don't draw for icons
  end

  local inLos = true
  if (self.enemyHit) then
    local x,y,z = Spring.GetUnitPosition(self.unit)
    if (x==nil) then return false end
    inLos = select(2, Spring.GetPositionLosState(x,y,z, LocalAllyTeamID))
  else
    local losState = Spring.GetUnitLosState(self.unit, LocalAllyTeamID) or {}
    inLos = (inLos)and(not losState.los)
  end
  return (inLos)and(Spring.IsUnitVisible(self.unit, 0, true)) -- Don't draw for icons
end

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

function UnitJitter.Create(Options)
  SetUnitLuaDraw(Options.unit,true)

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

function UnitJitter:Destroy()
  SetUnitLuaDraw(self.unit,false)
end

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

return UnitJitter