local _math = require "_math"
local mat4 = _math.mat4

local pixel_font_data = love.filesystem.newFileData("pixel_font.glsl")
local vertex_font_data = love.filesystem.newFileData("vertex_font.glsl")
local shader_font = love.graphics.newShader(pixel_font_data, vertex_font_data)

local fonts = {
   ter_6x12 = {
      path = "font/terminus_128x64_6x12.data",
      texture_width = 128,
      texture_height = 64,
      glyph_width = 6,
      glyph_height = 12,
   },
   ter_8x16 = {
      path = "font/terminus_128x128_8x16.data",
      texture_width = 128,
      texture_height = 128,
      glyph_width = 8,
      glyph_height = 16,
   },
   ter_10x18 = {
      path = "font/terminus_256x128_10x18.data",
      texture_width = 256,
      texture_height = 128,
      glyph_width = 10,
      glyph_height = 18,
   },
   ter_12x24 = {
      path = "font/terminus_256x128_12x24.data",
      texture_width = 256,
      texture_height = 128,
      glyph_width = 12,
      glyph_height = 24,
   }
}

local load_font = function(desc)
   local file_data = love.filesystem.newFileData(desc.path)
   local image_data = love.image.newImageData(desc.texture_width, desc.texture_height, "r8", file_data)
   local texture = love.graphics.newTexture(image_data)
   texture:setFilter("nearest", "nearest")
   local font = {
      texture = texture,
      texture_width = desc.texture_width,
      texture_height = desc.texture_height,
      glyph_width = desc.glyph_width,
      glyph_height = desc.glyph_height,
      stride = math.floor(desc.texture_width / desc.glyph_width),
   }
   return font
end

local glyph_coordinate = function(font, ord)
   local c = ord - 32
   local x = c % font.stride
   local y = math.floor(c / font.stride)
   return {x, y}
end

local glyph_transform = function(font, x, y)
   local transform = (
        mat4.scaling(font.glyph_width, font.glyph_height, 0)
      * mat4.translation(x, -y, 0)
      * mat4.scaling(2 / 1024, 2 / 1024, 0)
      * mat4.translation(-1, 1, 0)
   )
   return transform
end

local draw_string = function(font, s, x, y)
   love.graphics.setCanvas()
   love.graphics.setShader(shader_font)
   shader_font:send("texture_sampler", font.texture)
   love.graphics.setDepthMode("always", false)
   shader_font:send("cell", {font.glyph_width / font.texture_width, font.glyph_height / font.texture_height})

   for i = 1, #s do
      local c = s:byte(i)
      if c ~= 32 then
         shader_font:send("transform", "column", glyph_transform(font, x, y):data())
         shader_font:send("glyph", glyph_coordinate(font, c))
         love.graphics.drawFromShader(screen_index_buffer, 3 * 2, 1, 1)
      end

      x = x + font.glyph_width
   end
end

return {
   fonts = fonts,
   load_font = load_font,
   draw_string = draw_string,
}
