#!/usr/bin/env texlua
-----------------------------------------------------------------------
--         FILE:  mkcharacters.lua
--        USAGE:  ./mkcharacters.lua 
--  DESCRIPTION:  import parts of char-def.lua
-- REQUIREMENTS:  lua, ConTeXt, the lualibs package
--       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>
-----------------------------------------------------------------------
-- We create a stripped-down version of char-def.lua, suitable for use
-- with the generic font loader.
-----------------------------------------------------------------------

-----------------------------------------------------------------------
--                              config
-----------------------------------------------------------------------
local mkivpath      = arg[1]
local charfile      = arg[2] or "./build/luaotfload-characters.lua"

---  for every code point char-def.lua provides a set of fields. they
---  are:
---
---     * adobename
---     * category
---     * cjkwd
---     * comment
---     * contextname
---     * description
---     * direction
---     * lccode
---     * linebreak
---     * mathclass
---     * mathextensible
---     * mathfiller
---     * mathname
---     * mathspec
---     * mathstretch
---     * mathsymbol
---     * mirror
---     * shcode
---     * specials
---     * textclass
---     * uccode
---     * unicodeslot
---     * variants

local import = {
  "direction", "mirror", --> πολυγλωσσία/uax9
  "category",            --> https://gist.github.com/phi-gamma/5812290
  "textclass",           --> https://gist.github.com/phi-gamma/6488187 
}

-----------------------------------------------------------------------
--                             includes
-----------------------------------------------------------------------

kpse.set_program_name"luatex"

require "lualibs"

local chardef
local charini

if not mkivpath then
  mkivpath = assert (kpse.expand_path
                      "~/context/tex/texmf-context/tex/context/base/mkiv/",
                     "Failed to locate ConTeXt.")
end

chardef = mkivpath .. "/char-def.lua"
charini = mkivpath .. "/char-ini.lua"

--- we could grab the files from contextgarden but as Context is part
--- of TL it’s not worth bothering
if not (chardef and lfs.isfile(chardef)) then
  chardef = assert(kpse.find_file("char-def.lua", "lua"),
                   "Failed to locate file char-def.lua from ConTeXt.")
end

if not (charini and lfs.isfile(charini)) then
  charini = assert(kpse.find_file("char-ini.lua", "lua"),
                   "Failed to locate file char-ini.lua from ConTeXt.")
end

io.write(string.format("extracting data from char-def.lua at %s\n",
                       chardef))
io.write(string.format("loading code from char-ini.lua at %s\n",
                       charini))

-----------------------------------------------------------------------
--                           functionality
-----------------------------------------------------------------------

local get_characters = function ( )
  local data
  local inchan = io.open(chardef, "r")
  if not inchan then
    io.write("Could not open file for reading: "..chardef.."\n.")
    goto fail
  end
  data = inchan:read "*all"
  inchan:close()
  data = loadstring(data)
  if data then
    data() --> characters.data
    data = nil
    collectgarbage "collect"
    if characters.data and next(characters.data) then
      return characters.data
    end
    io.write "Character table empty.\n"
    goto fail
  end
  io.write(chardef .. " is not a valid Lua file.\n")
  ::fail::
  io.write "Emergency exit.\n"
  os.exit(1)
end

local extract_fields_indeed
extract_fields_indeed = function (data, acc, lastidx)
  local idx, char = next(data, lastidx)
  if idx then
    local imported = { }
    for i=1, #import do
      local field = import[i]
      imported[field] = char[field]
    end
    acc[idx] = imported
    return extract_fields_indeed(data, acc, idx)
  end
  return acc
end

local extract_fields = function (data)
  return extract_fields_indeed(data, {}, nil)
end

--[[ extract_classifiers : from luatex-basics-prepare.tex ]]

local extract_classifiers = function (chardata)
  dofile (charini)
  local s_init = 1    local s_rphf =  7
  local s_medi = 2    local s_half =  8
  local s_fina = 3    local s_pref =  9
  local s_isol = 4    local s_blwf = 10
  local s_mark = 5    local s_pstf = 11
  local s_rest = 6

  local mappers = {
    l = s_init,  -- left
    d = s_medi,  -- double
    c = s_medi,  -- joiner
    r = s_fina,  -- right
    u = s_isol,  -- nonjoiner
  }

  local first_arabic,  last_arabic  = characters.blockrange("arabic")
  local first_syriac,  last_syriac  = characters.blockrange("syriac")
  local first_mandiac, last_mandiac = characters.blockrange("mandiac")
  local first_nko,     last_nko     = characters.blockrange("nko")

  local classifiers = { }

  for k, c in next, chardata do
    if k > 0 then
      local c = chardata[k]
      if c then
        local arabic = c.arabic
        if arabic then
          classifiers[k] = mappers[arabic]
          elseif k >= first_arabic  and k <= last_arabic  or k >= first_syriac  and k <= last_syriac  or
            k >= first_mandiac and k <= last_mandiac or k >= first_nko     and k <= last_nko     then
            if c.category == "mn" then
              classifiers[k] = s_mark
            else
              classifiers[k] = s_rest
            end
          end
        end
      end
    end
    return classifiers
  end

local amend_table_fields = function (data, classifiers)
  --- installed by luatex-basics-prepare.tex
  data.characters  = { }
  data.classifiers = classifiers
  return data
end

local writedata = function (data)
  local outchan = io.open(charfile, "w")
  if not outchan then
    io.write("Could not open "..charfile.." for writing.\n")
    return false
  end
  outchan:write(data)
  outchan:close()
  return true
end

do
  local chardata    = get_characters()
  local classifiers = extract_classifiers(chardata)
  local stripped    = extract_fields(chardata)
  local amended     = amend_table_fields(stripped, classifiers)
  local serialized  = table.serialize(amended, true, {
    compact   = true,
    noquotes  = true,
    hexify    = true, --- for consistency with char-def
  })
  if writedata(serialized) then
    goto done
  end
  goto fail
end

::done::
  os.exit(0)

::fail::
  io.write "Emergency exit.\n"
  os.exit(1)

--- vim:ft=lua:ts=2:et:sw=2