local love = assert( love )
local mat4 = require( "mat4" )
local alph = 1/math.sqrt(2)
local testMesh = love.graphics.newMesh(
  {--attributes
    {"VertexPosition", "float", 4},
    {"VertexColor", "float", 3},
    {"VertexTexCoord", "float", 2},
  },
  {--vertices
    { 1, 0, -alph, 1,
      1, 1, 1,
      1, 0 },
    { -1, 0, -alph, 1,
      1, 0, 0,
      0, 0 },
    { 0, 1, alph, 1,
      0, 1, 0,
      1, 1, },
    { 0, -1, alph, 1,
      0, 0, 1,
      0, 1, },
  },
  "strip",
  "static"
)
testMesh:setVertexMap{ 1,2,3,4,1,2 }
local rockTexture = love.graphics.newImage( "tex/rock.png" )
rockTexture:setWrap( "repeat", "repeat" )
testMesh:setTexture( rockTexture )

local testQuad = love.graphics.newMesh(
  {--attributes
    {"VertexPosition", "float", 4},
    {"VertexColor", "float", 3},
    {"VertexTexCoord", "float", 2},
  },
  {--vertices
    { -1, -1,- 2, 1,
      1, 1, 1,
      0, 0, },
    { 1, -1,- 2, 1,
      1, 0, 0,
      1, 0, },
    { 1, 1,- 2, 1,
      0, 0, 1,
      1, 1, },
    { -1, 1,- 2, 1,
      0, 1, 0,
      0, 1, },
  },
  "fan",
  "static"
)

local testShader = [[
  varying float d;
#ifdef VERTEX
  uniform mat4 mdl;
  uniform mat4 view;
  uniform mat4 proj;
  vec4 position( mat4 _, vec4 pos ){
    vec4 cam = view*mdl*pos;
    d = cam.z;
    return proj * cam;
  }
#endif
#ifdef PIXEL
  vec4 effect( vec4 color, Image tex, vec2 texuv, vec2 scruv) {
    return color * Texel(tex, texuv) * vec4( 2.0, 2.0, 2.0, -d / 9.0 ) ;
  }
#endif
]]
testShader = love.graphics.newShader( testShader, testShader )

--column major
local function prod( A, B )
  local C = {}
  for j = 1,4 do C[j] = {0, 0, 0, 0} end
  for j = 1, 4 do
    for i = 1, 4 do
      C[j][i] =
      A[1][i] * B[j][1] +
      A[2][i] * B[j][2] +
      A[3][i] * B[j][3] +
      A[4][i] * B[j][4]
    end
  end
  return C
end


local function roll( t )
  local c, s = math.cos(t), math.sin(t)
  return {
    {c, 0, s, 0},
    {0, 1,  0, 0},
    {-s, 0, c, 0},
    {0, 0,  0, 1},
  }
end


local function trans( x, y, z )
  return {
    {1, 0, 0, 0},
    {0, 1, 0, 0},
    {0, 0, 1, 0},
    {x, y, z, 1},
  }
end

local function scale( x, y, z )
  return {
    {x, 0, 0, 0},
    {0, y, 0, 0},
    {0, 0, z, 0},
    {0, 0, 0, 1},
  }
end

local function id()
  return {
    {1, 0, 0, 0},
    {0, 1, 0, 0},
    {0, 0, 1, 0},
    {0, 0, 0, 1},
  }
end

--tait-bryan yaw pitch roll
local function rotate( a, b, g )
  local c, s = math.cos, math.sin
  local ca, sa, cb, sb, cg, sg = c(a), s(a), c(b), s(b), c(g), s(g)
  return {
    {           ca*cb,            sa*cb,   -sb, 0},
    {ca*sb*sg - sa*cg, sa*sb*sg + ca*cg, cb*sg, 0},
    {ca*sb*cg + sa*sg, sa*sb*cg - ca*sg, cb*cg, 0},
    {    0,                0,                0, 1},
  }
end

--fov in degrees, aspect w/h, near far
local function getProjectionMatrix(n, f, fov)
  local r = love.graphics.getWidth() / 2
  local t = love.graphics.getHeight() / 2
  local aspect = r/t
  local tan = math.tan( fov / 2 )
  return 
  --infinite z
  --TODO: get finite
  { {1.0 / tan, 0,   0,    0},
    {0, -1.0 / tan,  0,    0},
    {0, 0,  ( f + n ) / (n-f),  -1 },
    {0, 0,  -2 * f * n / (f - n),    0},
  }
end

local function getViewMatrix()
  return {
    {1, 0, 0, 0},
    {0, 1, 0, 0},
    {0, 0, 1, 0},
    {0, 0, 0, 1},
  }
end
  
local testModelMatrix
local testViewMatrix = getViewMatrix()
local testProjection = getProjectionMatrix( 0.1, 70, math.rad( 90 ) )
local time = 0

local testDSCanvas = love.graphics.newCanvas(
  love.graphics.getWidth(),
  love.graphics.getHeight(),
  { readable = true,
    format = "depth24" })

local testCanvas = love.graphics.newCanvas()
local creeFont = love.graphics.newFont( "fonts/bjcrus.ttf", 48 )
--row major
--4th column [:][4] translates
--diagonal [i][i], i < 4 scales
--rotations in [1:3][1:3]
function love.draw()
  local instance = require( "instance" )
  love.graphics.setCanvas( { testCanvas, depthstencil = testDSCanvas } ) 
  love.graphics.clear()
  love.graphics.setDepthMode( "less", true )
  instance.draw(testViewMatrix, testProjection)
  love.graphics.setCanvas()
  love.graphics.draw( testCanvas )
  --[[
  
  love.graphics.setColor( 1, 1 ,1, 1 )
  love.graphics.push( "all" )
  love.graphics.setCanvas( { testCanvas, depthstencil = testDSCanvas } )
  love.graphics.clear()
  love.graphics.setDepthMode( "less", true )
  love.graphics.setShader( testShader )
  testShader:send( "mdl",  "column", testModelMatrix )
  testShader:send( "proj", "column", testProjection )
  testShader:send( "view", "column", testViewMatrix )
  --love.graphics.draw( testMesh )
  
  math.randomseed( 4 )
  for i = 1, 15 do
    local x, y, z = 3 * ( math.random() - 0.5 ), 3 * ( math.random() - 0.5 )
    testShader:send( "mdl", "column",
      prod( trans( x, y, -8 ),
      rotate( 0, time, math.random() )))
    love.graphics.draw( testMesh )
  end
  
  testShader:send( "mdl", "column", id() )
 -- love.graphics.draw( testQuad )
  
  love.graphics.setShader()
  love.graphics.setColor(1,1,1, 0.5 )
  --love.graphics.rectangle( "fill", 0, 0, love.graphics.getDimensions() )
  love.graphics.setCanvas()
  love.graphics.setDepthMode( "always", true )
  love.graphics.pop()
  love.graphics.draw( testCanvas )
  local str = require( "i18n.en" )
  love.graphics.print( string.format( "%05.2f\t:TIME\n%05.2f\t:ANGLE\n", time,  time))
  love.graphics.print( { {1, 0.2, 0.2}, str.intro1},
    creeFont,
    0.5 * ( love.graphics.getWidth() - creeFont:getWidth(str.intro[1]) ),
    0.5 * ( love.graphics.getHeight() - creeFont:getHeight() ))]]
  --[[love.graphics.setShader()
  love.graphics.setColor( 1,1,1,1 )
  love.graphics.circle( "fill", time, time/2, 5 )]]
end

function love.update( dt )
  local instance = require("instance")
  time = time + dt
  testModelMatrix = prod(
    trans( 0, 0, -4 + math.sin( time )), 
    roll( time )
  )
  testModelMatrix = prod(
    testModelMatrix,
    rotate( time, time, time )
  )
  return instance.update()
end
