local readfile = require'luametalatex-readfile'

local sfnt = require'luametalatex-font-sfnt'
local stdnames = require'luametalatex-font-ttf-data'
local function round(x)
  local i, f = math.modf(x)
  if f < 0 then
    return i - (f<=-0.5 and 1 or 0)
  else
    return i + (f>=-0.5 and 1 or 0)
  end
end
local function addglyph(glyph, usedcids, cidtogid)
  if string.unpack(">i2", glyph) < 0 then -- We have a composite glyph.
    -- This is a mess. Disaster will follow.
    local offset = 11
    while offset do
      local flags, component = string.unpack(">I2I2", glyph, offset)
      local gid = cidtogid[component]
      if not gid then
        gid = #usedcids+1
        usedcids[gid] = {component}
        cidtogid[component] = gid
      end
      glyph = glyph:sub(1, offset+1) .. string.pack(">I2", gid-1).. glyph:sub(offset+4)
      offset = flags&32==32 and offset + 4 + (flags&1==1 and 4 or 2) + (flags&8==8 and 2 or (flags&64==64 and 4 or (flags&128==128 and 8 or 0)))
    end
  end
  return glyph
end
local function readpostnames(buf, i, usedcids, encoding)
  local usednames = {}
  local numglyphs
  numglyphs, i = string.unpack(">I2", buf, i)
  local stroffset = 2*numglyphs + i
  local names = setmetatable({}, {__index = function(t, i)
    for j=#t+1,i do
      t[j], stroffset = string.unpack("s1", buf, stroffset)
    end
    return t[i]
  end})
  local newusedcids = {}
  for j=1,#usedcids do
    local name = encoding[usedcids[j][1]]
    if name then
      local new = {}
      usednames[name], newusedcids[j] = new, new
    else
      newusedcids[j] = {j} -- FIXME: Someone used a character which does not exists in the encoding.
      -- This should probably at least trigger a warning.
    end
  end
  for j=1,numglyphs do
    local name
    name, i = string.unpack(">I2", buf, i)
    if name < 258 then
      name = stdnames[name]
    else
      name = names[name-257]
    end
    if usednames[name] then
      usednames[name][1] = j-1
      usednames[name] = nil
    end
  end
  if next(usednames) then
    error[[Missing character]]
  end
  return newusedcids
end
return function(filename, fontid, reencode)
  local file <close> = assert(readfile('truetype', filename))
  local buf = file()
  local magic, tables = sfnt.parse(buf, 1, fontid)
  if magic ~= "\0\1\0\0" then error[[Invalid TTF font]] end
  return function(fontdir, usedcids)
    if reencode and string.unpack(">I4", buf, tables.post[1]) == 0x00020000 then
      usedcids = readpostnames(buf, tables.post[1] + 32, usedcids, require'luametalatex-font-enc'(reencode))
    else
      usedcids = table.move(usedcids, 1, #usedcids, 1, {})
    end
    table.insert(usedcids, 1, {0})
    local newtables = {}
    newtables.head = buf:sub(tables.head[1], tables.head[1]+tables.head[2]-1)
    local bbox1, bbox2, bbox3, bbox4, scale, _
    scale, _, _, bbox1, bbox2, bbox3, bbox4 = string.unpack(">I2I8I8i2i2i2i2", buf, tables.head[1]+18)
    scale = 1000/scale
    fontdir.bbox = {math.floor(bbox1*scale), math.floor(bbox2*scale), math.ceil(bbox3*scale), math.ceil(bbox4*scale)}
    local cidtogid = {}
    for i=1,#usedcids do
      cidtogid[usedcids[i][1]] = i
    end
    local loca, glyf, locaOff, glyfOff = {}, {}, tables.loca[1], tables.glyf[1]
    hmtx = nil
    if string.unpack(">i2", buf, tables.head[1]+50) == 0 then -- short offsets
      local s, i = 0, 1
      while i <= #usedcids do
        local from, til = string.unpack(">I2I2", buf, locaOff+2*usedcids[i][1])
        loca[i] = string.pack(">I2", s)
        s = s+til-from
        glyf[i] = from ~= til and addglyph(buf:sub(glyfOff+from*2, glyfOff+til*2-1), usedcids, cidtogid) or ""
        i = i+1
      end
      loca[#usedcids+1] = string.pack(">I2", s)
    else -- long offsets
      local s, i = 0, 1
      while i <= #usedcids do
        local from, til = string.unpack(">I4I4", buf, locaOff+4*usedcids[i][1])
        loca[i] = string.pack(">I4", s)
        s = s+til-from
        glyf[i] = til == from and "" or addglyph(buf:sub(glyfOff+from, glyfOff+til-1), usedcids, cidtogid)
        i = i+1
      end
      loca[#usedcids+1] = string.pack(">I4", s)
    end
    newtables.loca = table.concat(loca)
    loca = nil
    newtables.glyf = table.concat(glyf)
    local hmtx = glyf
    glyf = nil
    for i = 1,#hmtx do hmtx[i] = nil end
    assert(tables.hhea[2] == 36)
    local numhmetrics = string.unpack(">I2", buf, tables.hhea[1]+34)
    newtables.hhea = buf:sub(tables.hhea[1], tables.hhea[1]+33) .. string.pack(">I2", #usedcids)
    local off = tables.hmtx[1]
    local finaladv, off2 = buf:sub(off+(numhmetrics-1)*4, off+numhmetrics*4-3), off+2*numhmetrics
    for i=1,#usedcids do
      if usedcids[i][1] < numhmetrics then
        hmtx[i] = buf:sub(off+usedcids[i][1]*4, off+usedcids[i][1]*4+3)
      else
        hmtx[i] = finaladv .. buf:sub(off2+usedcids[i][1]*2, off2+usedcids[i][1]*2+1)
      end
    end
    newtables.hmtx = table.concat(hmtx)
    newtables.maxp = buf:sub(tables.maxp[1], tables.maxp[1]+3) .. string.pack(">I2", #usedcids) .. buf:sub(tables.maxp[1]+6, tables.maxp[1]+tables.maxp[2]-1)
    if tables.fpgm then
      newtables.fpgm = buf:sub(tables.fpgm[1], tables.fpgm[1]+tables.fpgm[2]-1)
    end
    if tables.prep then
      newtables.prep = buf:sub(tables.prep[1], tables.prep[1]+tables.prep[2]-1)
    end
    if tables['cvt '] then
      newtables['cvt '] = buf:sub(tables['cvt '][1], tables['cvt '][1]+tables['cvt '][2]-1)
    end
    return sfnt.write(magic, newtables)
  end
end
