local ffi = require "ffi"
local _math = require '_math'
local mat4 = _math.mat4
local vec3 = _math.vec3
local vec4 = _math.vec4

local cube_indices = {
   0, 1, 2,
   3, 4, 5,
   6, 7, 8,
   9, 10, 11,
   12, 13, 14,
   15, 16, 17,
   0, 18, 1,
   3, 19, 4,
   6, 20, 7,
   9, 21, 10,
   12, 22, 13,
   15, 23, 16
}
local cube_indices_count = #cube_indices
local cube_index_buffer
local blocks
local shader_minecraft
local region_buffers = {}

local load_blocks_buffer = function(name)
   local data = love.filesystem.newFileData(name)
   local format = {
      { format = "uint8vec4" },
   }
   local buffer = love.graphics.newBuffer(format, data, { shaderstorage = true, usage = "static" })
   return buffer
end

local load_region_buffer = function(path)
   local data = love.filesystem.newFileData(path)
   local format = {
      { format = "uint8vec4" },
      { format = "uint32" },
   }
   local buffer = love.graphics.newBuffer(format, data, { shaderstorage = true, usage = "static" })
   return buffer
end

local load_cube_index_buffer = function()
   local data = love.data.newByteData(cube_indices_count * 4)
   local ptr = ffi.cast("uint32_t*", data:getFFIPointer())
   for i, index in ipairs(cube_indices) do
      ptr[i - 1] = index
   end
   local buffer = love.graphics.newBuffer("uint32", data, { index = true, usage = "static" })
   return buffer
end

local load_minecraft_shader = function()
   local pixel_data = love.filesystem.newFileData("pixel_minecraft.glsl")
   local vertex_data = love.filesystem.newFileData("vertex_minecraft.glsl")
   local shader = love.graphics.newShader(pixel_data, vertex_data)
   return shader
end

local init = function()
   table.insert(region_buffers, load_region_buffer("minecraft/region.0.0.data"))
   table.insert(region_buffers, load_region_buffer("minecraft/region.-1.0.data"))
   table.insert(region_buffers, load_region_buffer("minecraft/region.0.-1.data"))
   table.insert(region_buffers, load_region_buffer("minecraft/region.-1.-1.data"))
   cube_index_buffer = load_cube_index_buffer()
   shader_minecraft = load_minecraft_shader()
end

local viewpos = {
   x = -4 * 31,
   y = 0,
   z = -4 * 31,
}

local draw = function(projection)
   local view = mat4.look_at_rh(vec3(-50 + viewpos.x, -50 + viewpos.z, 100 + 50 + viewpos.y),
                                vec3(50 + viewpos.x, 50 + viewpos.z, 50 + viewpos.y),
                                vec3(0, 0, 1))

   love.graphics.setCanvas()

   love.graphics.setDepthMode("always", true)
   love.graphics.setShader(shader_clear)
   love.graphics.drawFromShader(screen_index_buffer, 3 * 2, 1, 1)

   love.graphics.setDepthMode("greater", true)
   love.graphics.setShader(shader_minecraft)

   local view_projection = view * projection
   local y_levels = 128
   --local instance_count = y_levels * 16 * 16
   -- for x = 0, 30 do
   --    for z = 0, 30 do
   --       local world = mat4.translation(x * 16, z * 16, 0)
   --       local transform = world * view_projection
   --       shader_minecraft:send("BlocksLayout", blocks[x][z])
   --       shader_minecraft:send("transform", "column", transform.data)
   --       love.graphics.drawFromShader(cube_index_buffer, cube_indices_count, instance_count, 1)
   --    end
   -- end

   for _, region_buffer in ipairs(region_buffers) do
      local instance_count = math.floor(region_buffer:getSize() / (4 * 2))
      shader_minecraft:send("transform", "column", view_projection:data())
      shader_minecraft:send("BlocksLayout", region_buffer)
      love.graphics.drawFromShader(cube_index_buffer, cube_indices_count, instance_count, 1)
   end
end

return {
   viewpos = viewpos,
   init = init,
   draw = draw,
}
