#!/usr/bin/env texlua
-----------------------------------------------------------------------
--         FILE:  mkstatus.lua
--        USAGE:  ./mkstatus.lua
--  DESCRIPTION:  writes the repository state
-- REQUIREMENTS:  luatex, the lualibs package
--       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com>
--      CREATED:  2013-07-07 14:01:12+0200
-----------------------------------------------------------------------
--
-- This script generates a list of hashes that serves as the input
-- for the file integrity check (option --diagnose). md5 is all we can
-- assume in Luatex, so it’s really only a superficial test.
--
-- Not that this file also incorporates the Git revision information.
-- Of this, only the actual commit hash is authoritative. The latest
-- tag is included for reference only. Besides that, it’s meaningless.
-- Do not rely on it: Two distinct versions of Luaotfload might very
-- well share the same tag.

kpse.set_program_name "luatex"

local md5 = require "md5"

require "lualibs"

local stringformat = string.format
local md5sumhexa   = md5.sumhexa
local ioloaddata   = io.loaddata
local iosavedata   = io.savedata
local iopopen      = io.popen
local iowrite      = io.write
local lfsisdir     = lfs.isdir
local stringmatch  = string.match

-----------------------------------------------------------------------
-- settings
-----------------------------------------------------------------------

local verbose  = false
local filelist = "./build/luaotfload-status.lua" --- result

local srcdir    = "src"
local builddir  = "build"
local scriptdir = "scripts"
local loaderdir = "src/fontloader"
local rtdir     = "src/fontloader/runtime"
local miscdir   = "src/fontloader/misc"

local names = {

  --- Luaotfload runtime files
  { srcdir,       "luaotfload-auxiliary.lua",       },
  { srcdir,       "luaotfload-colors.lua",          },
  { srcdir,       "luaotfload-configuration.lua",   },
  { srcdir,       "luaotfload-database.lua",        },
  { srcdir,       "luaotfload-diagnostics.lua",     },
  { srcdir,       "luaotfload-features.lua",        },
  { srcdir,       "luaotfload-init.lua",            },
  { srcdir,       "luaotfload-letterspace.lua",     },
  { srcdir,       "luaotfload-loaders.lua",         },
  { srcdir,       "luaotfload-log.lua",             },
  { srcdir,       "luaotfload-main.lua",            },
  { srcdir,       "luaotfload-parsers.lua",         },
  { srcdir,       "luaotfload-resolvers.lua",       },
  { srcdir,       "luaotfload-tool.lua",            },

  --- generated files
  { builddir,     "luaotfload-characters.lua",      },
  { builddir,     "luaotfload-glyphlist.lua",       },

  --- scripts
  { scriptdir,    "mkcharacters",                   },
  { scriptdir,    "mkglyphlist",                    },
  { scriptdir,    "mkimport",                       },
  { scriptdir,    "mkstatus",                       },
  { scriptdir,    "mktests",                        },

  --- fontloader runtimes
  { rtdir,        "fontloader-basics-gen.lua",      },
  { rtdir,        "fontloader-reference.lua",       },

  --- fontloader constituents
  { miscdir,      "fontloader-basics-nod.lua",      },
  { miscdir,      "fontloader-data-con.lua",        },
  { miscdir,      "fontloader-font-afk.lua",        },
  { miscdir,      "fontloader-font-onr.lua",        },
  { miscdir,      "fontloader-font-one.lua",        },
  { miscdir,      "fontloader-font-cid.lua",        },
  { miscdir,      "fontloader-font-con.lua",        },
  { miscdir,      "fontloader-font-def.lua",        },
  { miscdir,      "fontloader-font-ini.lua",        },
  { miscdir,      "fontloader-font-map.lua",        },
  { miscdir,      "fontloader-font-oti.lua",        },
  { miscdir,      "fontloader-font-gbn.lua",        },
  { miscdir,      "fontloader-font-def.lua",        },
  { miscdir,      "fontloader-fonts-demo-vf-1.lua", },
  { miscdir,      "fontloader-fonts-enc.lua",       },
  { miscdir,      "fontloader-fonts-ext.lua",       },
  { miscdir,      "fontloader-fonts.lua",           },
  { miscdir,      "fontloader-font-lua.lua",        },
  { miscdir,      "fontloader-fonts-syn.lua",       },
  { miscdir,      "fontloader-font-tfm.lua",        },
  { miscdir,      "fontloader-font-otr.lua",        },
  { miscdir,      "fontloader-font-cff.lua",        },
  { miscdir,      "fontloader-font-ttf.lua",        },
  { miscdir,      "fontloader-font-dsp.lua",        },
  { miscdir,      "fontloader-font-oup.lua",        },
  { miscdir,      "fontloader-font-otl.lua",        },
  { miscdir,      "fontloader-font-oto.lua",        },
  { miscdir,      "fontloader-font-otj.lua",        },
  { miscdir,      "fontloader-font-ota.lua",        },
  { miscdir,      "fontloader-font-ots.lua",        },
  { miscdir,      "fontloader-font-osd.lua",        },
  { miscdir,      "fontloader-font-ocl.lua",        },
  { miscdir,      "fontloader-font-otc.lua",        },

  --- lua libraries
  { miscdir,      "fontloader-languages.lua",       },
  { miscdir,      "fontloader-l-boolean.lua",       },
  { miscdir,      "fontloader-l-file.lua",          },
  { miscdir,      "fontloader-l-function.lua",      },
  { miscdir,      "fontloader-l-io.lua",            },
  { miscdir,      "fontloader-l-lpeg.lua",          },
  { miscdir,      "fontloader-l-lua.lua",           },
  { miscdir,      "fontloader-l-math.lua",          },
  { miscdir,      "fontloader-l-string.lua",        },
  { miscdir,      "fontloader-l-table.lua",         },
  { miscdir,      "fontloader-math.lua",            },
  { miscdir,      "fontloader-mplib.lua",           },
  { miscdir,      "fontloader-preprocessor.lua",    },
  { miscdir,      "fontloader-swiglib.lua",         },
  { miscdir,      "fontloader-swiglib-test.lua",    },
  { miscdir,      "fontloader-util-str.lua",        },
  { miscdir,      "fontloader-util-fil.lua",        },

} --[[local names]]

-----------------------------------------------------------------------
-- helpers
-----------------------------------------------------------------------

local die = function (...)
  io.stderr:write "[fatal error]: "
  io.stderr:write (stringformat (...))
  io.stderr:write "\naborting.\n"
  os.exit (1)
end

local logcmd = "git log -1 \z
                    --format=\"return {\z
                                %n  revision  = [[%H]],\z
                                %n  timestamp = [[%cd]],\z
                                %n  committer = [[%cn <%ce>]],\z
                                %n}\" \z
                    --date=iso"
local describecmd = "git describe --all"

local readpipe = function (cmd)
  local chan = iopopen (cmd)
  if not chan then
    die ("this script needs to be run inside \z
          the luaotfload git repository")
  end
  local data = chan:read "*all"
  chan:close ()
  return data
end

local git_info = function ()
  --io.write (logcmd)
  --io.write "\n"
  local desc = readpipe (describecmd)
  if not desc then die "cannot parse git information" end
  desc = string.fullstrip (desc)
  local desc = string.explode (desc, "/")[2] or desc
  if not desc then die "cannot parse sanitized git information" end
  local data = readpipe (logcmd)
  if data and type (data) == "string" and data ~= "" then
    data = load (data)
    if not data then
      die "cannot parse git information"
    end
    data = data ()
    data.description = desc
    return data
  end
  die "cannot read from pipe"
end

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

local hash_file = function (fname)
  if not lfs.isfile (fname) then
    die ("cannot find %s.", fname)
  end
  local raw = ioloaddata (fname)
  if not raw then
    die ("cannot read from %s.", fname)
  end
  return md5sumhexa (raw)
end

local hash_all
hash_all = function (list, acc)
  if acc == nil then
    local base = table.fastcopy (names)
    return hash_all (table.append (base, list), { })
  end

  local finfo = list[#list]
  list[#list] = nil
  if finfo then
    local fpath, fname
    if type (finfo) == "table" then
      local d, f = finfo [1], finfo [2]
      if lfs.isdir (d) then
        fpath = file.join (d, f)
      else
        fpath = f
      end
      fname = f
    else
      fpath = finfo
    end
    if verbose then
      iowrite "· md5("
      iowrite (fpath)
    end
    local sum = hash_file (fpath)
    if verbose then
      iowrite ") = \""
      iowrite (sum)
      iowrite "\"\n"
    end
    acc[#acc+1] = { fname, sum }
    return hash_all (list, acc)
  end
  return acc
end

local handle_argv = function (argv)
  local ret  = { files = { }, loader = nil }
  local argc = #argv
  if argc < 1 then return ret end
  local argoff = 1
  if argv [1] == "-v" then
    verbose = true
    if argc == 1 then return ret end
    argoff  = 2
  end
  local aux aux = function (acc, i)
    if i > argc then return acc else
      local cur = argv[i]
      if type (cur) == "string" then
        local loader = stringmatch (cur, "--fontloader=(.+)$")
        if loader then
          cur = loader
          acc.loader = file.basename (cur)
        end
        if lfs.isfile (cur) then
          local files = acc.files
          files[#files + 1] = cur
        end
      else
        die ("file not found: %s", tostring (cur))
      end
      return aux (acc, i + 1)
    end
  end
  return aux (ret, argoff)
end

local add_files
add_files = function (lst, acc)
  if lst == nil then return end
  if acc == nil then return add_files (lst, { }) end
  local len   = #lst
  if len == 0 then return acc end
  local cur   = lst[len]
  local fname = file.basename (cur)
  local path  = file.dirname (cur)
  acc[#acc + 1] = { path, fname }
  lst[len] = nil
  return add_files (lst, acc)
end

local main = function ()
  local raw_extra   = handle_argv (arg)
  local cuit_extra  = add_files (raw_extra.files)
  local hashes      = hash_all (cuit_extra)
  local notes       = git_info ()
  notes.loader      = raw_extra.loader
  local serialized  = table.serialize ({ notes = notes,
                                         hashes = hashes }, true)
  local success     = io.savedata (filelist, serialized)
  if success == false then
    die ("could not write to %s.", filelist)
  end
  return 0
end

return main ()

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