require "luascape.render"
require "materials"

shadows = {}

shadows.light = Light()
shadows.light.type = Light.DIRECTIONAL
shadows.light.translation = Vector(0, 500, 0)
shadows.light.rotation = Quaternion(Vector.AXIS_X, math.pi / 2)

shadows.shadowMap = nil
shadows.shadowTexture = nil
shadows.depthBuffer = nil

function createShadowPass(scene)
  local shadowPass = ShadowMapRenderPass()
  shadowPass.light = shadows.light
  shadowPass.scene = scene
  shadowPass:setResolution(settings.shadows.shadowMapSize, settings.shadows.shadowMapSize)
  shadowPass.alignToCamera = true
  shadowPass.fixedDepthBias = settings.shadows.fixedDepthBias
  shadowPass.slopeDepthBias = settings.shadows.slopeDepthBias
  shadows.light.width = settings.shadows.shadowRange
  shadows.light.height = settings.shadows.shadowRange
  if not shadows.shadowMap then
    shadows.shadowMap = shadowPass.shadowMap
  else
    shadowPass.shadowMap = shadows.shadowMap
  end
  return shadowPass
end

function createShadowPasses(renderer, rootNode)
  -- shadow light-space pass: create shadow-map
  local shadowPass = renderer.passes:add(createShadowPass(rootNode))
  shadowPass.renderTarget.viewport = Rectangle(1, 1, settings.shadows.shadowMapSize - 1, settings.shadows.shadowMapSize - 1)
--  local typoShadowPass = renderer.passes:add(shadowPass:clone())
--  typoShadowPass.scene = Act.currentAct().typoParentNode
--  typoShadowPass.clearFlags = 0
  if not settings.retail then
    InputManager.instance().bindings:add(ToggleRenderPassInputBinding(KeyboardState.KEY_9, shadowPass))
  end
  
  -- shadow pre-pass: draw shadows into texture
  local shadowPrePass = renderer.passes:add(DepthNormalPrePass())
  shadowPrePass.name = "depthNormalShadowPrePass"
  local shader = shadowPrePass.globalShader:clone()
  shader.shaderProgramState.vertexUniforms:add(ShadowMatrixUniform(shadowPass.light))
  shader.shaderProgramState.fragmentUniforms:add(VectorUniform(Vector(settings.fog.near, settings.fog.far, 0), "fog"))
  ShadowMapRenderPass.setupTextureState(shader:getOrCreateTextureState(), 0, shadowPass.shadowMap)
  shadowPrePass.globalShader = shader
  local shadowTextureTarget = TextureRenderTarget.createColorAlphaAndDepthTextureRenderTarget(1024, 1024)
  shadowPrePass.renderTarget = shadowTextureTarget
  if not settings.retail then
    InputManager.instance().bindings:add(ToggleRenderPassInputBinding(KeyboardState.KEY_F7, shadowPrePass))
  end

  -- blur shadow pass: blurs the shadow texture for soft shadows
  local blurPass = addBlurShadowPass(renderer, shadowTextureTarget:getColorTexture())
  if not settings.retail then
    InputManager.instance().bindings:add(ToggleRenderPassInputBinding(KeyboardState.KEY_0, blurPass))
  end
end

function createShadowPostPass(renderer)
  -- shadow post pass: renders shadows from the shadow texture over the frame
  local shadowPostPass = renderer.passes:add(QuadRenderPass())
  shadowPostPass.quad.material:getOrCreateTextureState():setTexture(0, render.shadowTexture)
  shadowPostPass.quad.material.shaderProgramState = graphics.createShaderProgramState(
    "fullscreen_quad_vp.cg", nil,
    "shadowpost_fp.cg", nil
  )
  graphics.applyBlending(shadowPostPass.quad, AlphaBlendState.BLEND_INV_SRC_ALPHA, AlphaBlendState.BLEND_SRC_ALPHA)
  shadowPostPass.enabled = not settings.perPixelHatch
end

function createGaussKernelUniform()
  local blurKernel = {
    1, 4, 6, 4, 1,
    4, 16, 24, 16, 4,
    6, 24, 36, 24, 6,
    4, 16, 24, 16, 4,
    1, 4, 6, 4, 1
  }
--  blurKernel = { 5, 6, 5 }
  local sum = 0
  for i,value in pairs(blurKernel) do sum = sum + value end
  local kernelUniform = FloatArrayUniform("coefficients")
  for i,value in pairs(blurKernel) do kernelUniform:add(value / sum) end
  return kernelUniform
end

function createShadowDebugPass(levelRenderPass)
  local pass = OrthogonalRenderPass()
  local scene = Scene()
  pass.scene = scene
  scene.scale = Vector(0.5, 0.5, 1)

  local quad = Quad()
  quad.material = SpriteMaterial(materials.getShadowMap(levelRenderPass))
  local node = scene.children:add(Node(quad))
  node.translation = Vector(-1, 1, 0)

  quad = Quad()
  quad.material = SpriteMaterial(materials.getShadowTexture(levelRenderPass))
  node = scene.children:add(Node(quad))
  node.translation = Vector(1, 1, 0)

  quad = Quad()
  quad.material = SpriteMaterial(materials.getBlurredShadowTexture(levelRenderPass))
  node = scene.children:add(Node(quad))
  node.translation = Vector(-1, -1, 0)
  return pass
end

function addBlurShadowPass(renderer, inputTexture)
  print "Creating blur shadow pass"
  local blurShadowPass = renderer.passes:add(BlurRenderPass())
  blurShadowPass.name = "blurShadowPass"
  blurShadowPass:setResolution(256, 256)
  blurShadowPass.pixelFormat = PixelFormat.RGBA8888
  blurShadowPass.bufferCount = 5
  blurShadowPass.renderTarget = TextureRenderTarget.createTextureRenderTarget()
  local shadowTexture = DynamicTexture(PixelFormat.RGBA8888)
  blurShadowPass.renderTarget.textures:add(shadowTexture)
  blurShadowPass.quad.material.textureState:setTexture(0, inputTexture)
  blurShadowPass.quad.material.shaderProgramState = graphics.createShaderProgramState(
    "fullscreen_quad_vp.cg", nil,
    "bluralpha_fp.cg", {
--      createGaussKernelUniform(),
--      VectorUniform(Vector(1.2 / inputTexture.width, 0, 0), "offset")
    }
  )
  return blurShadowPass
end
