#!/usr/bin/env texlua
require'pdfmml-emulate-node'
local convert = require'luamml-convert'
local mappings = require'luamml-legacy-mappings'
local to_xml = require'luamml-xmlwriter'

local parse_showlists = require'pdfmml-showlists'
local parse_log = require'pdfmml-logreader'

local text_families = {}

local attributes = lfs.attributes
local function try_extensions_(base, extension, ...)
  if extension == nil then return end
  local fullname = base .. extension
  if attributes(fullname, 'mode') == 'file' then
    return fullname
  end
  return try_extensions_(base, ...)
end
local function try_extensions(base, ...)
  if attributes(base, 'mode') == 'file' then return base end
  return try_extensions_(base, ...)
end

if #arg < 1 then
  io.stderr:write(string.format('Usage: %s {logname} [{outname}]\n\z
    If {outname} includes {}, then a separate file is written for every formula with {} replaced by the formula id.\n', arg[0]))
  os.exit(1)
end
local parsed = assert(parse_log(assert(try_extensions(arg[1], '.tml', '.log'),
      "Couldn't find input file.")))

for i, inst in ipairs(parsed.instructions) do
  local _, _, family, mapping_name = inst:find'^REGISTER_MAPPING:([0-9]+):(.*)$'
  if family then
    local mapping = mappings[mapping_name]
    if mapping then
      convert.register_family(tonumber(family), mapping)
    else
      io.stderr:write(string.format('Unknown mapping %s ignored\n', mapping_name))
    end
  else
    io.stderr:write'Unknown instruction ignored\n'
  end
end

local out_prefix, out_suffix, out_stream
if not arg[2] or arg[2] == '-' then
  out_stream = io.stdout
else
  local _ _, _, out_prefix, out_suffix = arg[2]:find'^(.*){}(.*)$'
  if not out_prefix then
    out_stream = assert(io.open(arg[2], 'w'))
  end
end
parsed.mathml = {}

local function shallow_copy(t)
  local new = {}
  for k, v in next, t do
    new[k] = v
  end
  return new
end

-- Currently only 3 flag values are supported:
--   0: Ignore (Doesn't make a lot of sense here)
--   1: Only save
--   3: Generate normally
for i, block in ipairs(parsed.groups) do
  local flag, tag, label = block.flag, block.tag, block.label
  block = block[1]
  if flag & 3 ~= 0 then
    local style = flag & 16 == 16 and flag>>5 & 0x7 or block.display and 0 or 2
    local xml = convert.process(parse_showlists(block, nil, nil, parsed), style, text_families)
    if flag & 2 == 2 then
      local stream = out_stream or assert(io.open(out_prefix .. tostring(i) .. out_suffix, 'w'))
      stream:write(to_xml(convert.make_root(shallow_copy(xml), style)), '\n')
      if not out_stream then stream:close() end
    end
    if tag ~= 'mrow' then
      if xml[0] == 'mrow' then
        xml[0] = tag
      else
        xml = {[0] = tag, xml}
      end
    end
    if (not block.display) and flag & 1 == 1 and label then
      if parsed.mathml[label] then
        error'Invalid label reuse'
      end
      parsed.mathml[label] = xml
    end
  end
end