-- texdoclib-util.tlu: utility functions for texdoc
--
-- The TeX Live Team, GPLv3, see texdoclib.tlu for details

-- dependencies
local texdoc = {
    const = require 'texdoclib-const',
    config = require 'texdoclib-config',
}

-- shortcuts
local M = {}
local C = texdoc.const

-- lowercase and change '/' to '\' on windows for display
-- Note: Internal representation of files always use forward slashes.
--       This function should be called only before displaying a path.
if os.type == 'windows' then
    function M.w32_path(path)
        return (string.gsub(string.lower(path), '/', '\\'))
    end
else
    function M.w32_path(path)
        return path
    end
end

-- remove the last directory component of a path
if os.type == 'windows' then
    function M.path_parent(path)
        return string.match(path, '^(.*)[\\/]')
    end
else
    function M.path_parent(path)
        return string.match(path, '^(.*)/')
    end
end

-- generic log display function
local function log(label, msg, ...)
    io.stderr:write('texdoc ' .. label .. ': ' .. msg:format(...) .. '\n')
end

-- generic error display function (see the err_priority constant)
function M.err_print(lvl, msg, ...)
    -- be careful: maybe config item "verbosity_level" has not set yet
    local verbosity_level = texdoc.config.get_value('verbosity_level')
        or tonumber(C.def_verbosity)

    -- print if the priority is higher than current verbosity level
    if C.err_priority[lvl] <= verbosity_level then
        log(lvl, msg, ...)
    end
end
local err_print = M.err_print

do -- begin scope of active_debugs
local active_debugs

-- set active_debugs
local function set_active_debugs()
    local debug_list = texdoc.config.get_value('debug_list')

    if not debug_list then return end
    active_debugs = {}

    -- all debug options imply version info
    if debug_list[1] then
        active_debugs.version = true
    else
        return
    end

    -- if 'all' is the first keyword, just activate all categories
    if debug_list[1] == 'all' then
        for dbg in pairs(C.known_debugs) do
            active_debugs[dbg] = true end
        return
    end

    -- activate options from the list
    for _, dbg in ipairs(debug_list) do
        local deps = C.known_debugs[dbg]
        if deps then
            active_debugs[dbg] = true
            for _, d in ipairs(deps) do active_debugs[d] = true end
        else
            err_print('warning', 'Unknown debug category "' .. dbg .. '".')
        end
    end
end

-- generic debug function
function M.dbg_print(cat, msg, ...)
    -- make sure active_debugs is set
    if not active_debugs then set_active_debugs() end

    -- print message it belongs to an active category
    if active_debugs and active_debugs[cat] or cat == 'XXX' then
        log('debug-' .. cat, msg, ...)
    end
end

end -- end scope of active_debugs

-- if filename is base .. '.' .. zip with zip in zipext_list, return: base, zip
-- otherwise, return: filename, nil
function M.parse_zip(filename)
    local zip
    for _, zip in ipairs(texdoc.config.get_value('zipext_list')) do
        local l = #zip + 1
        if string.sub(filename, -l, -1) == '.' .. zip then
            return string.sub(filename, 1, -l - 1), zip
        end
    end
    return filename, nil
end

-- take a known extension according to ext_list
function M.get_ext(filename)
    filename = M.parse_zip(filename)

    for _, e in ipairs(texdoc.config.get_value('ext_list')) do
        if e == '*' then
            local dot = filename:find('.', 1, true)
            if not dot then
                return ''
            else
                return filename:sub(-dot + 1)
            end
        elseif (e == '') then
            if not filename:find('.', 1, true) then
                return ''
            end
        else
            local dot_e = '.' .. e
            if filename:sub(-#dot_e) == dot_e then
                return dot_e:sub(2)
            end
        end
    end
end

-- print a usage message
function M.print_usage()
    local groups = {
        action = {},
        mode = {},
        interaction = {},
        debug = {},
    }
    for _, opt in ipairs(C.options) do
        local line = ''
        local shortopt = ''
        local longopt = ''
        if opt['short'] then
            if opt['short'] == 'D' then
                opt['long'] = 'debug'
            end
            if opt['metavar'] then
                shortopt = '-' .. opt['short'] .. ' ' .. opt['metavar']
            else
                shortopt = '-' .. opt['short']
            end
        end
        if opt['long'] then
            if opt['metavar'] then
                local sep = '='
                if opt['group'] == 'action' then
                    sep = ' '
                end
                longopt = '--' .. opt['long'] .. sep .. opt['metavar']
            else
                longopt = '--' .. opt['long']
            end
        end
        if #shortopt > 0 and #longopt > 0 then
            line = '  ' .. shortopt .. ', ' .. longopt
        elseif #shortopt > 0 then
            line = '  ' .. shortopt
        elseif #longopt > 0 then
            line = '  ' .. longopt
        end
        if #line > 20 then
            line = line .. "\n"
        end
        for _ = 1, 20 - #string.gsub(line, ".*\n", '') do
            line = line .. ' '
        end
        line = line .. opt['desc']
        table.insert(groups[opt['group']], line)
    end
    local usage_msg = C.usage_msg
    for k, v in pairs(groups) do
        usage_msg = string.gsub(usage_msg, '{{' .. k .. '}}', table.concat(v, "\n"))
    end
    print(usage_msg)
end

function M.print_zsh_completion()
    local groups = {
        action = {},
        mode = {},
        interaction = {},
        debug = {},
    }
    local option = ''
    local complete = ''
    local choices = {}
    local prefix = ''
    for _, opt in ipairs(C.options) do
        local stop_completions = {opt['group']}
        if opt['group'] == 'action' then
            stop_completions = {'-', ':', '*'}
        end
        prefix = '"(' .. table.concat(stop_completions, ' ') .. ')"'
        if opt['short'] and opt['long'] then
            if opt['long'] == 'debug' then
                option = '{-' .. opt['short'] .. ',--' .. opt['long'] .. '=-}'
            else
                option = '{-' .. opt['short'] .. ',--' .. opt['long'] .. '}'
            end
        elseif opt['short'] then
            option = '-' .. opt['short']
        elseif opt['long'] then
            option = '--' .. opt['long']
        end
        option = prefix .. option .. '"[' .. opt['desc'] .. ']'
        if opt['type'] == 'string' then
            if opt['complete'] == 'options' then
                choices = {}
                for _, v in pairs(C.known_options) do
                    v = string.gsub(v, '%.%*', '')
                    table.insert(choices, v)
                end
            elseif opt['complete'] == 'debugs' then
                choices = {}
                for i, _ in pairs(C.known_debugs) do
                    table.insert(choices, i)
                end
            elseif type(opt['complete']) == 'table' then
                choices = opt['complete']
            end
            table.sort(choices)
            complete = '(' .. table.concat(choices, ' ') .. ')'
            if opt['complete'] == 'files' then
                opt['metavar'] = ' '
                complete = '_files'
            end
            option = option .. ':' .. string.lower(opt['metavar']) .. ':' .. complete
        end
        option = option .. '"'
        table.insert(groups[opt['group']], option)
    end
    local completion = C.zsh_completion
    for k, v in pairs(groups) do
        completion = string.gsub(completion, '{{' .. k .. '}}', table.concat(v, "\n    "))
    end
    print(completion)
end

return M

-- vim: ft=lua:
