local dkjson = require( "lib.dkjson" )
local love = assert( love )
local shader = require( "shaders.flat" )
local mat = require( "mat4" )

local attributeNames = {
  POSITION = "VertexPosition",
  NORMAL = "VertexNormal",
}

local types = {
  ["SCALAR"] = 1,
  ["VEC3"] = 3,
}

local componentTypes = {
  [5121] = "byte",
  [5123] = "unorm16",
  [5126] = "float",
}

local elementComponentTypes = {
  [5123] = "uint16",
  [5126] = "uint32",
}

local asset
local buffer
do
  local fd = assert( love.filesystem.newFileData( "models/contempra.gltf" ) )
  asset = dkjson.decode( fd:getString() )
  buffer = love.filesystem.newFileData( "models/contempra.bin" )
end

--load all the bufferViews
local bufferViews = {}
for i, view in pairs( asset.bufferViews ) do
  bufferViews[i] = love.data.newByteData( buffer, view.byteOffset, view.byteLength )
end

local function attributeFormat( attributeName, accessor )
  return {attributeNames[attributeName], componentTypes[accessor.componentType], types[accessor.type] }
end

local function lgMesh( primitive )
  assert( not( primitive.mode ) ) --triangles only

  local finalMesh
  for name, accIdx in pairs( primitive.attributes ) do
    local acc = asset.accessors[accIdx+1]

    local mesh = love.graphics.newMesh(
      {attributeFormat(name, acc)}, --vertexformat
      acc.count,
      "triangles",
      "static"
    )

    local bfv = bufferViews[1+acc.bufferView]
    mesh:setVertices( bfv )

    if finalMesh then
      finalMesh:attachAttribute( attributeNames[name], mesh )
    else
      finalMesh = mesh
    end

  end


  do
    local acc = asset.accessors[1+primitive.indices]
    local bfv = bufferViews[1+acc.bufferView]
    finalMesh:setVertexMap(bfv, elementComponentTypes[acc.componentType] )
  end
  return finalMesh
end

--flat hierarchy only, ignores rotation. quaternions, man :( 
local function getNodeTransform( node )
  if not node.scale then return end
  return mat.TRS(
    node.scale[1], node.scale[2], node.scale[3],
    0, 0, 0,
    node.translation[1], node.translation[2], node.translation[3] )
end

local meshes = {}
local transforms = { mat.id() }
for _, nodeIdx in pairs( asset.scenes[1].nodes ) do
  local node = asset.nodes[1+nodeIdx]
  local mesh = asset.meshes[1+node.mesh]
  meshes[mesh.name] = lgMesh( mesh.primitives[1] )
  transforms[mesh.name] = getNodeTransform( node ) or transforms[1]
end


local obj = {
  isRinging = false,
  isTalking = false,
  x = 2,
  y = 0.25,
  z = -2,
  size = 0.1,
}


local colors = setmetatable(
  {
    Handset = {0.9, 0.1, 0.1 },
    Phone = {0.85, 0.1, 0.1 },
    Table = {0.30, 0.1, 0.1 },
    Plane = {0.14, 0.1, 0.1 },
    Veil = {1.0, 1.0, 1.0, 1.0 },
  },
  {__index = function() return { 0.3, 0, 0 } end}
)

local shaders = {
  Veil = require( "shaders.veil" )
}

local function veilAlpha( x, z )
  return math.max( 0, math.min( 1.0, 1.0 - (x*x+z*z - 3.0) / 90.0))
end

function obj.draw( view, proj, light, x, z  )
  if veilAlpha( x, z ) > 0.0 then
    love.graphics.setDepthMode( "less", false )
      local s = shaders.Veil
      love.graphics.setShader( s )
      s:send( "view", "column", view )
      s:send( "proj", "column", proj )
      love.graphics.setColor( 0, 0, 0, veilAlpha( x, z ) )
      love.graphics.draw( meshes.Veil )
    love.graphics.setBlendMode( "alpha" )
    love.graphics.setDepthMode( "less", true )
  end
  
  shader:send( "view", "column", view )
  shader:send( "proj", "column", proj )
  shader:send( "light", light )
  love.graphics.setShader( shader )
  for name, mesh in pairs( meshes ) do
    love.graphics.setColor( colors[name] )
    if name ~= "Veil" and 
    ( name ~= "Handset" or not obj.pickedUp ) then
      shader:send( "mdl", "column", transforms[name] )
      love.graphics.draw( mesh )
    end
  end
end

function obj.update( player )

end

function obj.pickup( )
  obj.pickedUp = true
end

return obj