\ProvidesPackage{mqref}
\RequirePackage{mqlua}
\RequirePackage{mqfqname}

\newcommand\luaDoubleFieldValue[3]{%
    \directlua{
        if #1 \string~= nil and #1[#2] \string~= nil and #1[#2][#3] \string~= nil then
            tex.sprint(#1[#2][#3])
            return
        end
        luatexbase.module_warning('luaDoubleFieldValue', 'Invalid indices to `#1`: `#2` and `#3`');
        tex.sprint("???")
    }%
}

% LABELS
\begin{luacode}
    -- Contains <label>: <true> for defined labels
    -- This could later be extended to contain a list of all fqnames that
    -- reference the label to make a network of references or sth like that
    labels = labels or {}
    -- Table of all labels that dont exist but were referenced
    -- <label>: <fqname where it was referenced>
    missingLabels = {}
    -- aux file with labels for completion in vim
    labelsFilepath = OUTDIR .. "/labels.txt" or "/tmp/labels.txt"
    labelsLuaFilepath = OUTDIR .. "/labels.lua.txt" or "/tmp/labels.lua.txt"
    -- aux file for debugging
    missingLabelsFilepath = OUTDIR .. "/missing-labels.txt" or "/tmp/missing-labels.txt"
    function labelExists(label)
        if labels[label] == nil then return false else return true end
    end
    function labelSet(label)
        labels[label] = true
    end
    if fileExists(labelsLuaFilepath) then
        labels = dofile(labelsLuaFilepath) or {}
    end
\end{luacode}

\begin{luacode*}
    function serializeKeyValues(tbl)
        local result = {}
        -- sort by keys making a new table with keys as values and sorting that
        for k, v in pairs(tbl) do
            table.insert(result, k)
        end
        table.sort(result)
        s = ""
        for i, k in ipairs(result) do
            s = s .. k .. "\tin\t" .. tbl[k] .. "\n"
        end
        return s
    end

    function dumpTableKeyValues(tableobj, filepath)
        table.sort(tableobj)
        local file = io.open(filepath, "w")
        file:write(serializeKeyValues(tableobj))
        file:close()
    end

    function serializeKeys(tbl)
        local result = {}
        -- sort by keys making a new table with keys as values and sorting that
        for k, v in pairs(tbl) do
            table.insert(result, k)
        end
        table.sort(result)
        return table.concat(result, "\n")
    end

    function dumpTableKeys(tableobj, filepath)
        table.sort(tableobj)
        local file = io.open(filepath, "w")
        file:write(serializeKeys(tableobj))
        file:close()
    end
\end{luacode*}

\AtEndDocument{\directlua{dumpTableKeys(labels, labelsFilepath)}}
\AtEndDocument{\directlua{dumpTable(labels, labelsLuaFilepath)}}
\AtEndDocument{\directlua{dumpTableKeyValues(missingLabels, missingLabelsFilepath)}}

% Set a label and write the label to the aux file
% [1] 
\newcommand\mqfqname@label[1][\fqname]{
    \label{#1}
    \directlua{labelSet(\luastring{#1})}
}

% REFERENCES
% All xyzRef commands link to the key using the translated name
% Uppercase (XyzRef) commands have different link texts, but the same link target
% 1: key/fully qualified name (without qty/eq/sec/const/el... prefix)

\begin{luacode*}
    function hyperref(target, text)
        local s = ""
        if labelExists(target) then
            s = "\\hyperref[" .. target .. "]"
        else  -- mark as missing and referenced in current section
            missingLabels[target] = fqnameGet()
        end
        if text == "" then
            tex.sprint(s .. "{" .. tlGetFallbackCurrent(target) .. "}")
        else
            tex.sprint(s .. "{" .. text .. "}")
        end
    end
\end{luacode*}


% Equations/Formulas
% \newrobustcmd{\fqEqRef}[1]{%
\newrobustcmd{\fAbsRef}[2][]{%
  \directlua{hyperref(\luastring{#2}, \luastring{#1})}%
}

\begin{luacode*}
    function translateRelativeFqname(target)
        local relN = 0

        local relTarget = ""
        warning('translateRelativeFqname', '(target=' .. target .. ') ');
        for i = 1, #target do
            local c = target:sub(i,i)
            if c == ":" then
                relN = relN + 1
            else
                relTarget = target:sub(i,#target)
                break
            end
        end
        if relN == 0 then
            return target
        end
        
        local N = fqnameGetDepth()
        local newtarget = fqnameGetN(N - relN + 1) .. ":" .. relTarget
        warning('translateRelativeFqname', '(relN=' .. relN .. ') ' .. newtarget);
        return newtarget
    end
\end{luacode*}
\newcommand{\fRef}[2][]{
    \directlua{hyperref(translateRelativeFqname(\luastring{#2}), \luastring{#1})}
}
% [1]: link text
% 2: number of steps to take up
% 3: link target relative to the previous fqname section
\newcommand{\mqfqname@fRelRef}[3][1]{
    \directlua{
        local N = fqnameGetDepth()
        luatexbase.module_warning('fRelRef', '(N=' .. N .. ') #2');
        if N > #2 then
            local upfqname = fqnameGetN(N-#2)
            hyperref(upfqname .. \luastring{:#3}, \luastring{#1})
        else
            luatexbase.module_warning('fUpRef', 'fqname depth (N=' .. N .. ') too low for fUpRef if #1');
        end
    }
}
\newcommand{\fThisRef}[2][]{\mqfqname@fRelRef[#1]{0}{#2}}
\newcommand{\fUpRef}[2][]{\mqfqname@fRelRef[#1]{1}{#2}}
\newcommand{\fUppRef}[2][]{\mqfqname@fRelRef[#1]{2}{#2}}

% Quantities
% <symbol>
\newrobustcmd{\qtyRef}[1]{%
    \edef\tempname{\luaDoubleFieldValue{quantities}{"#1"}{"linkto"}}%
    \hyperref[qty:#1]{\GT{\tempname:#1}}%
}
% <symbol> <name>
\newrobustcmd{\QtyRef}[1]{%
    $\luaDoubleFieldValue{quantities}{"#1"}{"symbol"}$ \qtyRef{#1}%
}
% Constants
% <name>
\newrobustcmd{\constRef}[1]{%
    \edef\tempname{\luaDoubleFieldValue{constants}{"#1"}{"linkto"}}%
    \hyperref[const:#1]{\GT{\tempname:#1}}%
}
% <symbol> <name>
\newrobustcmd{\ConstRef}[1]{%
    $\luaDoubleFieldValue{constants}{"#1"}{"symbol"}$ \constRef{#1}%
}
% Element from periodic table
% <symbol>
\newrobustcmd{\elRef}[1]{%
    \hyperref[el:#1]{{\color{fg0}#1}}%
}
% <name>
\newrobustcmd{\ElRef}[1]{%
    \hyperref[el:#1]{\GT{el:#1}}%
}



% "LABELS"
% These currently do not place a label,
% instead they provide an alternative way to reference an existing label
\directLuaAux{
    absLabels = absLabels or {}
    abbrLabels = abbrLabel or {}
}
% [1]: translation key, if different from target
% 2: target (fqname to point to)
% 3: key
\newcommand{\absLink}[3][\relax]{
    \directLuaAuxExpand{
        absLabels["#3"] = {}
        absLabels["#3"]["fqname"] = [[#2]] 
        absLabels["#3"]["translation"] = [[#1]] or [[#2]]
        % if [[#1]] == "" then
        %     absLabels["#3"]["translation"] = [[#2]]
        % else
        %     absLabels["#3"]["translation"] = [[#1]]
        % end
    } 
}
% [1]: target (fqname to point to)
% 2: key
% 3: label (abbreviation)
\newcommand{\abbrLink}[3][sec:\fqname]{
    \directLuaAuxExpand{
        abbrLabels["#2"] = {}
        abbrLabels["#2"]["abbr"] = [[#3]] 
        abbrLabels["#2"]["fqname"] = [[#1]] 
    } 
}

\newcommand{\fLabel}


% [1]: text
% 2: key
\newcommand{\absRef}[2][]{%
    \directlua{
        if absLabels["#2"] == nil then
            tex.sprint(string.sanitize(\luastring{#2}) .. "???")
        else
            if \luastring{#1} == "" then  %-- if [#1]  is not given, use translation of key as text, else us given text
                tex.sprint("\\hyperref[" .. absLabels["#2"]["fqname"] .. "]{\\GT{" .. absLabels["#2"]["translation"] .. "}}")
            else
            tex.sprint("\\hyperref[" .. absLabels["#2"]["fqname"] .. "]{\luaescapestring{#1}}")
            end
        end
    }
}
\newrobustcmd{\abbrRef}[1]{%
    \directlua{
        if abbrLabels["#1"] == nil then
            tex.sprint(string.sanitize(\luastring{#1}) .. "???")
        else
            tex.sprint("\\hyperref[" .. abbrLabels["#1"]["fqname"] .. "]{" .. abbrLabels["#1"]["abbr"] .. "}")
        end
    }
}
