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

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/spike.gltf" ) )
  asset = dkjson.decode( fd:getString() )
  buffer = love.filesystem.newFileData( "models/spike.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 spike = lgMesh( asset.meshes[1].primitives[1] )
spike:setTexture( love.graphics.newImage( "tex/rock.png", { mipmaps = true } ) )

local spikeCount = 4096
do

  local function rad(x,z)
    return x*x+z*z
  end

  local spikes = {}
  local inner, outer = 25, 100
  for n = 1, spikeCount do
    local r, a = math.sqrt( math.random() * (outer*outer-inner*inner)+outer*outer ), 2 * math.pi * math.random()
    local x, z = r*math.cos(a),r*math.sin(a)
    local h = 2 + 10 * math.random()
    spikes[n] = { x, -1, z, h, 0 }
    n=n+1
  end
  local spikeInstanceMesh = love.graphics.newMesh(
    {{"spikeInstance", "float", 4},
      {"spikeOrientation", "float", 1 },
    }, --vertexformat
    spikes,
    nil,
    "static"
  )
  spike:attachAttribute( "spikeInstance", spikeInstanceMesh, "perinstance" )
  spike:attachAttribute( "spikeOrientation", spikeInstanceMesh, "perinstance" )
end

local spikes = {}
function spikes.draw( view, proj, light )
  love.graphics.setColor( 1,1,1,1)
  shader:send( "view", "column", view )
  shader:send( "proj", "column", proj )
  shader:send( "light", light )
  love.graphics.setShader( shader )
  love.graphics.drawInstanced( spike, spikeCount )
end


return spikes