module = "seatingchart"

checkengines = { "luatex" }
stdengine = "luatex"

sourcefiles = { "*.dtx" }
unpackfiles = { "*.dtx" }
installfiles = {
  "*.sty",
  "*.lua",
  "*.sc"
}

typesetexe = "lualatex"
typesetopts = "-interaction=nonstopmode -shell-escape"
typesetruns = 3
typesetfiles = {
  "seatingchart.dtx",
  "seatingchart-example.tex"
}
typesetsuppfiles = {
  "*.sty",
  "*.cls"
}

--[[ 
  Include the ready-to-install tree in the CTAN upload.  In particular, this
  carries the files generated from the dtx source (.sty, .lua, and .sc). 
]]
packtdszip = true



docfiles = {
  "seatingchart-en.pdf",
  "seatingchart-de.pdf",
  "seatingchart-example.pdf",
  "seatingchart-example.tex",
  "example-infile.csv"
}

--[[ 
  build.lua should be part of the release sources so that the unpacked CTAN archive
  can run the same documentation and regression-test targets. 
]]
textfiles = {
  "README.md",
  "build.lua"
}

excludefiles = {
  "*~",
  "config-*.lua"
}

cleanfiles = {
  "*-cnltx*",
  "*.toc",
  "*.aux",
  "*.idx",
  "*.ilg",
  "*.ind"
}

tagfiles = { "seatingchart.dtx" }

--[[ 
  The documentation is maintained in one bilingual .dtx source,
  langselect obtains the target language from the second component of the job name. 
]]
function typeset(file, dir, cmd)
  dir = dir or "."
  local extension = file:match("%.([^.]*)$")
  local jobs

  if extension == "dtx" then
    jobs = { module .. "-en", module .. "-de" }
  else
    jobs = { file:match("([^/]+)%.[^.]+$") }
  end

  for _, job in ipairs(jobs) do
    local errorlevel
    for run = 1, typesetruns do
      errorlevel = tex(file, dir, cmd .. " -jobname=" .. job)
      if errorlevel ~= 0 then
        return errorlevel
      end

      if run == 1 and extension == "dtx" then
        makeindex(job, dir, ".idx", ".ind", ".ilg", indexstyle)
      end

      if run > 1 and not rerun_needed(job, dir) then
        break
      end
    end
  end

--[[ 
  l3build normally derives *one* PDF name from each typeset source.
  However, we use langselect. Thus, the dtx driver deliberately
  creates one PDF *per language* (in this case English and German).
  Thus, we publish both names explicitly where the CTAN collector
  expects documentation files. 
]]
  if extension == "dtx" then
    for _, job in ipairs(jobs) do
      local errorlevel = cp(job .. ".pdf", dir, docfiledir)
      if errorlevel ~= 0 then
        return errorlevel
      end
    end
  end

  return 0
end

function rerun_needed(job, dir)
  local logfile = io.open(dir .. "/" .. job .. ".log", "r")
  if not logfile then
    return false
  end
  local log = logfile:read("*all")
  logfile:close()

  return log:find("Rerun to get cross%-references right")
    or log:find("Label%(s%) may have changed")
    or log:find("There were undefined references")
    or log:find("Rerun LaTeX")
end

--[[ 
  Preserve the development layout for files which l3build does not normally
  ship in a CTAN source archive.  Keeping these directories intact makes the
  extracted archive immediately usable with `l3build check` and `l3build doc`. 
]]
local l3build_copyctan = copyctan
function copyctan()
  local errorlevel = l3build_copyctan()
  if errorlevel and errorlevel ~= 0 then
    return errorlevel
  end

  local package_dir = ctandir .. "/" .. ctanpkg
  local release_dirs = {
    { source = supportdir, target = package_dir .. "/support",
      patterns = { "*.sty", "*.cls" } },
    { source = testfiledir, target = package_dir .. "/testfiles",
      patterns = { "*.lvt", "*.tlg" } },
    { source = testsuppdir, target = package_dir .. "/testfiles/support",
      patterns = { "*.csv" } }
  }

  for _, directory in ipairs(release_dirs) do
    mkdir(directory.target)
    for _, pattern in ipairs(directory.patterns) do
      errorlevel = cp(pattern, directory.source, directory.target)
      if errorlevel ~= 0 then
        return errorlevel
      end
    end
  end

  return 0
end

--[[
  Collect all user-facing artifacts in build/result instead of leaving
  generated files in the package root.
]]
local l3build_ctan = ctan
function ctan()
  local errorlevel = l3build_ctan()
  if errorlevel ~= 0 then
    return errorlevel
  end

  mkdir(resultdir)
  local artifacts = {
    { name = "seatingchart-en.pdf", source = docfiledir },
    { name = "seatingchart-de.pdf", source = docfiledir },
    { name = "seatingchart-example.pdf", source = docfiledir },
    { name = ctanzip .. ".zip", source = ctandir },
    { name = ctanpkg .. ".tds.zip", source = tdsdir }
  }
  for _, artifact in ipairs(artifacts) do
    if fileexists(artifact.source .. "/" .. artifact.name) then
      rm(resultdir, artifact.name)
      cp(artifact.name, artifact.source, resultdir)
    end
    rm(currentdir, artifact.name)
  end

  return 0
end

-- target_list captures the original function before build.lua is loaded.
-- Point the public target explicitly at the wrapper above.
target_list.ctan.func = ctan

local function update_lua_tag(content, tagname, tagdate)
  local updated = content:gsub(
    "(Date:%s*\n%s*)%d%d%d%d%-%d%d%-%d%d",
    "%1" .. tagdate
  )
  updated = updated:gsub(
    "(Version:%s*\n%s*)v?[%w%.%-]+",
    "%1" .. tagname
  )
  return updated
end

function update_tag(file, content, tagname, tagdate)
  if not tagname then
    local handle = io.popen("git describe --tags --abbrev=0")
    tagname = handle:read("*a"):match("[^\n]+")
    handle:close()
    print("Set tagname to '" .. tagname .. "'")
  end

  --[[
    l3build passes --date through without validation or normalisation. 
    We accept both common input forms and derive the format required by each target.
  ]]
  local iso_date = tagdate:gsub("/", "-")

  if file:match("%.lua$") then
    return update_lua_tag(content, tagname, iso_date)
  end

  if file:match("%.dtx$") then
    local package_date = iso_date:gsub("-", "/")
    local updated = content:gsub(
      "(\\ProvidesExplPackage%s*{seatingchart}%s*\n%s*{)"
        .. "%d%d%d%d/%d%d/%d%d"
        .. "(}%s*\n%s*{)[^}%s]+(})",
      "%1" .. package_date .. "%2" .. tagname .. "%3",
      1
    )

--[[
  Lua has no \Provides... declaration. Restrict its independent metadata
  update to the docstrip guard so that no other embedded file becomes a
  second source for the package version.
  NOTE: Has to be adapted in case of several lua files.
]]
    updated = updated:gsub(
      "(%%<%*lua>\n)(.-)(\n%%</lua>)",
      function(opening, lua, closing)
        return opening .. update_lua_tag(lua, tagname, iso_date) .. closing
      end,
      1
    )
    return updated
  end

  return content
end
