local dkjson = require( "lib.dkjson" )
local love = assert( love )
local lg = assert( love.graphics )
local math = math
local mat = require( "mat4" )


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

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

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

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

local filterTypes = {
  [9729] = "linear",
  [9987] = "linear",
}

local asset
local buffer
do
  local fd = assert( love.filesystem.newFileData( "models/bell.gltf" ) )
  asset = dkjson.decode( fd:getString() )
  buffer = love.filesystem.newFileData( "models/bell.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 {
    assert(attributeNames[attributeName]),
    assert(componentTypes[accessor.componentType]),
    assert(types[accessor.type]) }
end

local function lgTexture( matIdx )
  local tex = asset.textures[1+matIdx]
  local img = asset.images[1+tex.source]
  local smpl = asset.samplers[1+tex.sampler]
  img = love.graphics.newImage( "tex/"..img.uri, { mipmaps = true } )
  img:setWrap( "repeat", "repeat" )
  img:setFilter( filterTypes[smpl.minFilter], filterTypes[smpl.magFilter] )
  img:setMipmapFilter( filterTypes[smpl.minFilter] )
  return img
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
  
  if primitive.material then finalMesh:setTexture( lgTexture( primitive.material ) ) end
  return finalMesh
end


local bellMesh = lgMesh( asset.meshes[1].primitives[1] )
--each bell has the following positional data:
-- scale, x, z, colorparam
local bells = {}
local bellInstanceMesh 
do
  local c, s = math.cos( 2*math.pi / 7 ), -math.sin( 2*math.pi / 7 )
  
  local x, z = 1, 0
  local r = 15
  for i = 1, 7 do
    bells[i] = { i / 2, r * x, r * z, (i-1)/6 }
    x, z = c*x+s*z, c*z-s*x
  end
  bellInstanceMesh = lg.newMesh({{"bellInstance","float",4}}, bells, nil, "static" )
  bellMesh:attachAttribute( "bellInstance", bellInstanceMesh,
    "perinstance" )
end

local t = {}

t.shader = require( "shaders.bell" )
t.mesh = bellMesh

function t.draw( view, proj, light )
  local s = t.shader
  s:send( "view", "column", view )
  s:send( "proj", "column", proj )
  s:send("light", light )
  lg.setShader(s)
  lg.drawInstanced( t.mesh, 7 )
end

function t.transpose( i, j )
  print( "transposing:", i, j )
  io.flush()
  bells[i][2], bells[i][3], bells[j][2], bells[j][3] = 
  bells[j][2], bells[j][3], bells[i][2], bells[i][3]
  bellInstanceMesh:setVertices( bells ) 
  bellMesh:attachAttribute( "bellInstance", bellInstanceMesh,
    "perinstance" )
end

return t