% The UCW Macro Collection: References
% Written by Martin Mares <mj@ucw.cz> in 2018 and placed into public domain
% -------------------------------------------------------------------------

% Should clickable links be produced?
\newif\ifclickable
\clickabletrue

% We maintain a collection of objects. Each object has:
%    - type (namespace -- e.g., chapter)
%    - identifier (alphanumeric string used to refer to the object in TeX source)
%    - label (to be typeset in clickable links -- e.g., chapter number)
%    - location (it is tied to a specific place in the output document)

% Some output formats (e.g., EPUB) have output split to multiple files.
% We need to keep track of the file name for every object.
% When producing a PDF, it is always empty.
\def\refcurrentfile{}

% An auxiliary file to which we write definitions of all identifiers
\newwrite\ids
\def\writeid#1#2{\immediate\write\ids{\string\iddef{#1}{#2}{\refcurrentfile}}}
\def\delayedwriteid#1#2{\write\ids{\string\iddef{#1}{#2}{\refcurrentfile}}}

% Used in auxiliary files
\def\iddef#1#2#3{\ifcsname id:#1\endcsname\ucwwarn{Identifier #1 re-defined}\else
\expandafter\def\csname id:#1\endcsname{#2}%
\expandafter\def\csname ff:#1\endcsname{#3}%
\fi}

% Read the auxiliary file from the previous run of TeX, create a new one
\immediate\openin\ids=\jobname.ids.aux
\ifeof\ids
\else
\input \jobname.ids.aux
\fi
\immediate\closein\ids
\immediate\openout\ids=\jobname.ids.aux

% Define an object. It is called from macros with optional arguments
% as \addid{type}{label}. It defines a new object whose identifier is the
% optional argument.
% It sets \currentid to either ID prefixed by its type, or to \relax.
\def\addid#1#2{%
	\ifx\optarg\relax
		\let\currentid\relax
	\else
		\edef\currentid{#1\optarg}%
		\writeid{\currentid}{#2}%
		\ifclickable
			\pdfdest name {\currentid} xyz\relax
		\fi
	\fi
}

% A low-level interface for typesetting references: produces a clickable link
% to the identifier #2 of type #1 with a label translated by macro #3 (use \relax
% for no translation).
\def\reflink#1#2#3{\expandafter\ifx\csname id:#1#2\endcsname\relax
	{\ifx\bo\undefined\bf\else\bo\fi ??}%
	\immediate\write16{*** Warning: Reference #1 undefined ***}%
\else
	\ifclickable
		\pdfstartlink\commonlinkargs goto name {#1#2}\relax
	\fi
	#3{\csname id:#1#2\endcsname}%
	\ifclickable
		\pdfendlink\relax
	\fi
\fi
}

% Common style of all clickable links
\pdflinkmargin=1pt
\def\commonlinkargs{height \the\dimexpr\ht\strutbox-0.5pt\relax depth \the\dimexpr\dp\strutbox-0.5pt\relax attr {/C [0 0 0.5] /Border [0 0 2]}}

% Typeset a link to identifier #2 of type #1
\def\ref#1#2{\reflink{#1}{#2}\relax}

% Define an identifier of type page pointing to the current page
\def\pageid#1{\delayedwriteid{page#1}{\the\count0}\ifclickable\pdfdest name {page#1} fit\relax\fi}
\def\pageref{\ref{page}}

% Typeset a reference to the given page number (used in tables of contents and indices,
% where we know the page number from other sources).
\def\pagelink#1{\ifclickable\pdfstartlink\commonlinkargs goto page #1 {/Fit}\relax #1\pdfendlink\else #1\fi}

% Typeset a clickable URL
% Currently, all weird characters must be properly escaped.
\def\url#1{%
	\leavevmode
	\ifclickable
		\pdfstartlink\commonlinkargs user {/Subtype/Link /A << /Type/Action /S/URI /URI(#1) >>}\relax
	\fi
	\xurl #1^^X%
	\ifclickable
		\pdfendlink\relax
	\fi
}
\def\xurl#1:#2#3^^X{{\I #1:#2\ifx#2/\kern-0.1em\fi#3}}
