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

\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 == nil or 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})}%
}

\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}[2][]{%
    % \edef\tempname{\luaDoubleFieldValue{quantities}{"#1"}{"fqname"}}%
    % \hyperref[qty:#1]{\GT{\tempname}}%
    \directlua{hyperref(quantityGetFqname(\luastring{#2}), \luastring{#1})}
}
% <symbol> <name>
\newrobustcmd{\QtyRef}[2][]{%
    $\quantity@getSymbol{#2}$ \qtyRef{#2}{}%
}
% Constants
% <name>
\newrobustcmd{\constRef}[2][]{%
    % \edef\tempname{\luaDoubleFieldValue{constants}{"#1"}{"linkto"}}%
    % \hyperref[const:#1]{\GT{\tempname}}%
    \directlua{hyperref(constantGetFqname(\luastring{#2}), \luastring{#1})}
}
% <symbol> <name>
\newrobustcmd{\ConstRef}[2][]{%
    $\constant@getSymbol{#2}$ \constRef{#2}%
}
% 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
\begin{luacode}
absLabels = absLabels or {}
abbrLabels = abbrLabels or {}

function absLabelAdd(key, target, translationKey)
    absLabels[key] = {
        fqname = (target == "") and fqnameGet() or target,
        translation = translationKey or ""
    }
end

function absLabelGetTarget(key)
    if absLabels[key] then
        return absLabels[key].fqname or "abs:" .. key
    else
        return "abs:" .. key
    end
end

function absLabelGetTranslationKey(key)
    if absLabels[key] then
        return absLabels[key].translation or ""
    else
        return ""
    end
end


function abbrLabelAdd(key, target, label)
    abbrLabels[key] = {
        abbr = label,
        fqname = (target == "") and fqnameGet() or target
    }
end

function abbrLabelGetTarget(key)
    if abbrLabels[key] then
        return abbrLabels[key].fqname or "abbr:" .. key
    else
        return "abbr:" .. key
    end
end

function abbrLabelGetAbbr(key)
    if abbrLabels[key] then
        return abbrLabels[key].abbr or ""
    else
        return ""
    end
end
\end{luacode}

% [1]: translation key, if different from target
% 2: target (fqname to point to), if left empty will use current fqname
% 3: key
\newcommand{\absLink}[3][]{
    \directLuaAuxExpand{
        absLabelAdd(\luastring{#3}, \luastring{#2}, \luastring{#1})
    }
}

% [1]: target (fqname to point to)
% 2: key
% 3: label (abbreviation)
\newcommand{\abbrLink}[3][]{
    \directLuaAuxExpand{
        abbrLabelAdd(\luastring{#2}, \luastring{#1}, \luastring{#3})
    }
}



% [1]: text
% 2: key
\newcommand{\absRef}[2][]{%
    \directlua{
        local text = (\luastring{#1} == "") and absLabelGetTranslationKey(\luastring{#2}) or \luastring{#1}
        if text \string~= "" then
            text = tlGetFallbackCurrent(text)
        end
        hyperref(absLabelGetTarget(\luastring{#2}, text))
    }%
}
\newrobustcmd{\abbrRef}[1]{%
    \directlua{hyperref(abbrLabelGetTarget(\luastring{#1}), abbrLabelGetAbbr(\luastring{#1}))}
        % if abbrLabels["#1"] == nil then
        %     tex.sprint(string.sanitize(\luastring{#1}) .. "???")
        % else
        %     tex.sprint("\\hyperref[" .. abbrLabels["#1"]["fqname"] .. "]{" .. abbrLabels["#1"]["abbr"] .. "}")
        % end
    % }
}
