require "luascape.graphics"
require "luascape.objects"
require "luascape.scene"
require "luascape.logic"
require "luascape.modules.fx"
require "luascape.modules.cg"
require "tinyandbig"
require "settings"

materials = {}

function materials.getShadowMapRenderPass(levelRenderPass)
  return levelRenderPass.renderer:findFirstRenderPass("shadowMapRenderPass")
end

function materials.getShadowMap(levelRenderPass)
  local shadowMapRenderPass = materials.getShadowMapRenderPass(levelRenderPass)
  if (shadowMapRenderPass) then
    return shadowMapRenderPass.shadowMap
  else
    return nil
  end 
end

function materials.getShadowLight(levelRenderPass)
  local shadowMapRenderPass = materials.getShadowMapRenderPass(levelRenderPass)
  if (shadowMapRenderPass) then
    return shadowMapRenderPass.light
  else
    return Light()
  end 
end

-- Shadow texture is a screen-sized grayscale texture which contains shadow values
function materials.getShadowTexture(levelRenderPass)
  local shadowPrePass = levelRenderPass.renderer:findFirstRenderPass("depthNormalShadowPrePass")
  if (shadowPrePass) then
    return shadowPrePass.renderTarget.textures[0]
  else
    return nil
  end 
end

function materials.getBlurredShadowTexture(levelRenderPass)
  local blurShadowPass = levelRenderPass.renderer:findFirstRenderPass("blurShadowPass")
  if (blurShadowPass) then
    return blurShadowPass.renderTarget.textures[0]
  else
    return nil
  end 
end

function materials.getDepthBuffer(levelRenderPass)
  local depthNormalPrePass = levelRenderPass.renderer:findFirstRenderPass("depthNormalShadowPrePass")
  if (depthNormalPrePass) then
    return depthNormalPrePass.renderTarget:getDepthTexture()
  else
    return nil
  end 
end


------------------------------------------------
-- lava material 
------------------------------------------------

function materials.createLavaMaterial()
  local shader = Shader()

  local timeUniform = TimeUniform()
  timeUniform.factor = 1
  Pacemaker.instance().updateables:add(timeUniform)    
  shader.shaderProgramState = graphics.createShaderProgramState(
  "lavalake_vp.cg", {
    timeUniform,
    ModelViewProjectionMatrixUniform(),
    ModelMatrixUniform(),
  },
  "lavalake_fp.cg", { }                   
  )
   
  local textureState = shader:getOrCreateTextureState()
  local baseMap = Texture.loadFromFile("lava_textur.jpg")
  textureState:setTexture(0, baseMap)
  return shader
end

function materials.applyLavaMaterial(lavanode)
  if (not lavanode.visual) then
    return
  end
  lavanode.visual.material = Preloader.instance():getShader("materials.lava")

  local node, system, crowd = particleSystem.create()
  graphics.applyBlending(crowd, AlphaBlendState.BLEND_SRC_ALPHA, AlphaBlendState.BLEND_ONE)
  
  lavanode.children:add(node)
  graphics.applyTexture(crowd, "glow.png")
  
  system.maxParticleCount = 1600
  system.output = 400
  system.emitter = VertexEmitter(lavanode.visual)
  system.emitter.offset = Vector(0,5,0)
  system.velocity = Vector(0,10,0)
  system.velocityRange = Vector(0.0,5,0.0)
  system.lifetime = 4
  system.alphaOutFading = true
  system.alphaInFading = true
  system.gravity = Vector(0,0,0)
  system:setScale(10,10)
  system:setRotation(0,0)
end


------------------------------------------------
-- crystal material 
------------------------------------------------

function materials.createCrystalEdgeMaterial()
  local shader = Shader()
  local texture = Texture.loadFromFile("cuttingedge.png")
  shader:getOrCreateTextureState():setTexture(0, texture)
  return shader
end

function materials.createCrystalMaterial()
  local standardShader = Shader()
--[[
  standardShader.shaderProgramState = graphics.createShaderProgramState(
    "crystal_vp.cg", {
      CameraTranslationUniform("eyePosition"),
      VectorUniform(Vector(100, 100, 100), "lightPosition"),
      ModelViewProjectionMatrixUniform(),
      ModelMatrixUniform(),
    },
    "crystal_fp.cg", {
      FloatUniform(0.5, "reflectivity")
    }                   
  )
  ]]--
  local textureState = graphics.createTextureState("test.png")
  --[[
--  local cubeMap = CubeMap()
--  cubeMap:loadFaces("sb_front.png", "sb_back.png", "sb_left.png", "sb_right.png", "sb_top.png", "sb_bottom.png")
--  textureState:setTexture(1, cubeMap)
--  textureState:setTexGen(1, TextureUnit.TEXGEN_REFLECTION_MAP)
  local envMap = Texture.loadFromFile("panorama_env.png")
  textureState:setTexture(1, envMap)
--  textureState:setTexGen(0, TextureUnit.TEXGEN_SPHERE_MAP)
  ]]--
  standardShader.textureState = textureState
  return standardShader
end

function materials.applyCrystalMaterial(node, scene, renderer, light)
--[[
  local meshNormalizer = MeshNormalizer()
  node.castsShadows = true
  meshNormalizer.normalOperation = MeshNormalizer.GENERATE_FACE_NORMALS  
  local visuals = objects.getVisuals(node)
  for i,visual in pairs(visuals) do
    meshNormalizer:apply(visual)
  end
  objects.applyMaterial(node, Preloader.instance():getShader("materials.crystal"))
  ]]--
  
  materials.applyHatchingMaterial(node, light, "test.png")
end

------------------------------------------------
-- tiny material 
------------------------------------------------
function materials.createTinyMaterial()
  local shader = Shader()
  local shaderProgramState = graphics.createShaderProgramState(
    "tiny_vp.cg", {
      CameraTranslationUniform("eyePosition"),
      NodeTranslationUniform(shadowLight, "lightPosition"),
      ModelViewProjectionMatrixUniform(),
      ModelMatrixUniform(),
    },
    "tiny_fp.cg", {
      FloatUniform(100, "shininess")
    }                   
  )
  shader.shaderProgramState = shaderProgramState
  return shader
end


function materials.applyTinyMaterial(visual, light)
  local shader = Preloader.instance():getShader("materials.tiny")
  objects.applyRenderState(visual, shader.shaderProgramState)
end



-----------------------------------------------
--- Typo material
-----------------------------------------------

function materials.createTypoMaterial(texture) 
  local shader = Shader()
  local shaderProgramState = shader:getOrCreateShaderProgramState()
  shaderProgramState.vertexProgram = ShaderProgram.loadShaderProgram("blop_vp.cg", ShaderProgram.TYPE_VERTEX_PROGRAM)
  shaderProgramState.fragmentProgram = ShaderProgram.loadShaderProgram("blop_fp.cg", ShaderProgram.TYPE_FRAGMENT_PROGRAM)
  
  shader:getOrCreateLightState().enabled = false
  shader:getOrCreateTextureState():setTexture(0, Texture.loadFromFile(texture))
  return shader
end

-----------------------------------------------
--- Cross section material
-----------------------------------------------

function materials.createCrossSectionMaterial()
  local shader = Shader()
  local textureState = shader:getOrCreateTextureState()
  textureState:setTexture(0, Texture.loadFromFile("explosion.png"))

  local alphaBlendState = shader:getOrCreateAlphaBlendState()
  alphaBlendState.sourceFactor =  AlphaBlendState.BLEND_SRC_ALPHA
  alphaBlendState.destinationFactor =  AlphaBlendState.BLEND_INV_SRC_ALPHA
  alphaBlendState.enabled = true

  local lightState = shader:getOrCreateLightState()
  lightState.enabled = false
  return shader
end 

function materials.applyCrossSectionMaterial(visual)
  visual.material = Preloader.instance():getShader("materials.crosssection")
end

function materials.createRopeHUDMaterial()
  local shader = Shader()
  
  shader:getOrCreateCullState().mode = CullState.CULL_NONE
  shader:getOrCreateDepthBufferState().enabled = false
  shader:getOrCreateLightState().enabled = false
  
  local alphaState = shader:getOrCreateAlphaBlendState()
  alphaState.sourceFactor = AlphaBlendState.BLEND_SRC_ALPHA
  alphaState.destinationFactor = AlphaBlendState.BLEND_INV_SRC_ALPHA
  alphaState.enabled = true

  local textureState = shader:getOrCreateTextureState()
  textureState:setTexture(0, Texture.loadFromFile("crosshairjedi.png"))
  textureState.enabled = true
  
  return shader
end

function materials.createCutLineMaterial()
  local lineShader = Shader()
  lineShader:getOrCreateCullState().mode = CullState.CULL_NONE

  local alphaState = lineShader:getOrCreateAlphaBlendState()
  alphaState.sourceFactor = AlphaBlendState.BLEND_SRC_ALPHA
  alphaState.destinationFactor = AlphaBlendState.BLEND_INV_SRC_ALPHA
  alphaState.enabled = true

  local depthBuffer = lineShader:getOrCreateDepthBufferState()
  depthBuffer.writable = false
  depthBuffer.enabled = true


  lineShader:getOrCreateLightState().enabled = false
  
  local textureState = lineShader:getOrCreateTextureState()
  textureState:setMatrix(0, Matrix.scaleMatrix(6,1,1))
  textureState:setTexture(0, Texture.loadFromFile("line.png"))
  textureState:setMipMapping(0, TextureUnit.MIPMAP_NONE)
  textureState.enabled = true


  local shaderProgramState = lineShader:getOrCreateShaderProgramState()
  shaderProgramState.fragmentProgram = ShaderProgram.loadShaderProgram("cutline_fp.cg", ShaderProgram.TYPE_FRAGMENT_PROGRAM)
  
  return lineShader
end

function materials.createCutHUDMaterial()
  local hudShader = Shader()
  
  hudShader:getOrCreateCullState().mode = CullState.CULL_NONE
  hudShader:getOrCreateDepthBufferState().enabled = false
  hudShader:getOrCreateLightState().enabled = false
  
  
  local alphaState = hudShader:getOrCreateAlphaBlendState()
  alphaState.sourceFactor = AlphaBlendState.BLEND_SRC_ALPHA
  alphaState.destinationFactor = AlphaBlendState.BLEND_INV_SRC_ALPHA
  alphaState.enabled = true

  local cullState = hudShader:getOrCreateCullState()
  cullState.mode = CullState.CULL_NONE

  local textureState = hudShader:getOrCreateTextureState()
  textureState:setTexture(0, Texture.loadFromFile("crosshairsplit.png"))
  textureState.enabled = true
  
  return hudShader
end
