% \iffalse
% vim: set expandtab:
% vim: set shiftwidth=2:
% vim: set tabstop=2:
% \fi
% \iffalse meta-comment
%
% Copyright (C) 2026 by Lukas Heindl <oss.heindl+latex@protonmail.com>
% ---------------------------------------------------------------------------
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2008/05/04 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Lukas Heindl.
%
% This work consists of all files listed in manifest.txt.
%
% \fi
%
% \iffalse
%<*driver>
\ProvidesFile{hexdumptikz-annotate.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[2022-06-01]
%
%<*driver>
\begin{document}
  \DocInput{\jobname.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%
% \changes{v0.0.0}{2026-05-13}{First draft}
%
%</driver>
% \fi
%
% \iffalse
%<*package>
%<@@=hexdumptikz_annotate>
% \fi
%
% \maketitle
%
% \begin{abstract}
%   Functions for annotating a hexdump after it has been drawn.
% \end{abstract}
%
% Identify the package and give the over all version information.
%    \begin{macrocode}
\ProvidesExplPackage {hexdumptikz-annotate} {2026-06-16} {1.0.0}
  {Annotate hexdumps}
%    \end{macrocode}
%
% Load the internal libraries which are used
%    \begin{macrocode}
\RequirePackage { hexdumptikz-addrcalc }
%    \end{macrocode}
%
% \begin{var}{\l_@@_start_row_tl}
% \begin{var}{\l_@@_start_col_tl}
% \begin{var}{\l_@@_end_row_tl}
% \begin{var}{\l_@@_end_col_tl}
% variables used when labeling a hexdump
%    \begin{macrocode}
\tl_new:N \l_@@_start_row_tl
\tl_new:N \l_@@_start_col_tl
\tl_new:N \l_@@_end_row_tl
\tl_new:N \l_@@_end_col_tl
%    \end{macrocode}
% \end{var}
% \end{var}
% \end{var}
% \end{var}
%
% \begin{var}{\l_@@_start_row_int}
% \begin{var}{\l_@@_end_row_int}
% \begin{var}{\l_@@_middle_n_row_tl}
% \begin{var}{\l_@@_middle_s_row_tl}
% variables used for drawing multi-line spanning annotations
%    \begin{macrocode}
\int_new:N \l_@@_start_row_int
\int_new:N \l_@@_end_row_int
\tl_new:N \l_@@_middle_n_row_tl
\tl_new:N \l_@@_middle_s_row_tl
%    \end{macrocode}
% \end{var}
% \end{var}
% \end{var}
% \end{var}
%
% \begin{var}{\l_@@_tmpa_tl}
% Variable to avoid clobbering the usual scratch variables.
%    \begin{macrocode}
\tl_new:N \l_@@_tmpa_tl
%    \end{macrocode}
% \end{var}
%
% \begin{fn}{\l_@@_offset_lookup:nnN}
% When drawing the hexdump, it stored a mapping from offset to (y-)index.
% This function allows a lookup in this mapping allowing to \enquote{calculate} with the offset as it becomes an index.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_int} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & key (base-address / y-component) \\
%   2 & \ain  & some context which is printed with the error \\
%   3 & \aout & int variable to store the (y-)index in \\
%   - & \ain  & \texttt{g\_hexdumptikz\_common\_cur\_offsets\_prop} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_offset_lookup:nnN #1 #2 #3
{
  \prop_get:NnNTF \g_hexdumptikz_common_cur_offsets_prop
  { #1 }
  \l_@@_tmpa_tl
  {
    \int_set:Nn #3 { \l_@@_tmpa_tl }
  }
  {
    \msg_critical:nnnn
    { hexdumptikz }
    { unknown-row }
    { #1 }
    { #2 }
  }
}
\cs_generate_variant:Nn \@@_offset_lookup:nnN { enN }
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\l_@@_label_bytes_init:nnn}
% Some common functionalities which are required by multiple annotation functions.
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & pgfkeys for the line \\
%   2 & \ain  & start-address \\
%   3 & \ain  & end-address \\
%   - & \aout & \texttt{l\_@@\_start\_row\_tl} \\
%   - & \aout & \texttt{l\_@@\_start\_col\_tl} \\
%   - & \aout & \texttt{l\_@@\_end\_row\_tl} \\
%   - & \aout & \texttt{l\_@@\_end\_col\_tl} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_label_bytes_init:nnn #1 #2 #3
{
  \pgfkeys {
    /hexdumptikz,
    #1
  }
  \hexdumptikz_address_to_nodename_components:nNN
    { #2 }
    \l_@@_start_row_tl
    \l_@@_start_col_tl
  \hexdumptikz_address_to_nodename_components:nNN
    { #3 }
    \l_@@_end_row_tl
    \l_@@_end_col_tl
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\l_@@_label_bytes:nnn}
% Enclose a sequence of bytes with a line.
%
% This does not list sideeffects as there are many variables affected by this.
% On the other hand, the code is enclosed by a fresh group, so changes are local anyhow.
%
% \begin{args}
%   1 & \ain  & pgfkeys for the line \\
%   2 & \ain  & start-address \\
%   3 & \ain  & end-address \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_label_bytes:nnn #1 #2 #3
{
%    \end{macrocode}
% grouping needed to avoid pgfkeys leaking options
%    \begin{macrocode}
  \group_begin:
%    \end{macrocode}
%
% common code across multiple annotation functionalities
%    \begin{macrocode}
  \@@_label_bytes_init:nnn { #1 } { #2 } { #3 }
%    \end{macrocode}
%
% check if the annotation spans multiple rows/lines $\to$ maybe the drawing can be simplified
%    \begin{macrocode}
  \str_if_eq:VVTF
  \l_@@_start_row_tl
  \l_@@_end_row_tl
  {
    \begin{ scope }[/hexdumptikz/next~scope]
      \draw
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl   .north~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl   .south~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .south~west) -- % noqa: S103
        cycle
        ;
    \end{ scope }
  }
  {
%    \end{macrocode}
% the annotation spans multiple rows/lines $\to$ obtain the y-indices so calculations are possible
%    \begin{macrocode}
    \@@_offset_lookup:enN
      { 0x \l_@@_start_row_tl }
      { start }
      \l_@@_start_row_int
    \@@_offset_lookup:enN
      { 0x \l_@@_end_row_tl }
      { end }
      \l_@@_end_row_int
%    \end{macrocode}
%
% check if there is a \enquote{true} middle row. Else use the start-/end-row as replacement
%
% Note two middle rows are important:
% \begin{enumerate}
%   \item the topmost middle row \texttt{middle\_n\_row} (\emph{north}) -- the row to which a vertical line from the \emph{end-row} extends to
%   \item the bottommost middle row \texttt{middle\_s\_row} (\emph{south}) -- the row to which a vertical line from the \emph{begin-row} extends to
% \end{enumerate}
%
%    \begin{macrocode}
    \int_compare:nNnTF
      { \l_@@_start_row_int + 1 }
      =
      { \l_@@_end_row_int }
      {
        \tl_set:Nn
          \l_@@_middle_n_row_tl
          { \int_to_arabic:n { \l_@@_end_row_int } }
        \tl_set:Nn
          \l_@@_middle_s_row_tl
          { \int_to_arabic:n { \l_@@_start_row_int } }
      }
      {
        \tl_set:Nn
          \l_@@_middle_n_row_tl
          { \int_to_arabic:n { \l_@@_start_row_int + 1 } }
        \tl_set:Nn
          \l_@@_middle_s_row_tl
          { \int_to_arabic:n { \l_@@_end_row_int - 1 } }
      }
%    \end{macrocode}
%
% Do the actual drawing (starting at north west, going clockwise)
%    \begin{macrocode}
    \begin{ scope }[/hexdumptikz/next~scope]
      \draw
%    \end{macrocode}
% top left corner to top right corner of the first line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (0,0) -- (1,0);
%   \node[fill=red,circle] at (0,0) {1};
%   \node[fill=red,circle] at (1,0) {2};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl     .north~west) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl -end                               .north~east) -- % noqa: S103
%    \end{macrocode}
% bottom right corner of the lowest fully captured line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (1,-1) -- (0.5,-1);
%   \node[fill=red,circle] at (1,-1) {3};
%   \node[fill=red,circle] at (0.5,-1) {4};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz-out- \tl_use:N \l_@@_middle_s_row_tl -end                           .south~east) -- % noqa: S103
        (hexdumptikz-out- \tl_use:N \l_@@_middle_s_row_tl - \tl_use:N \l_@@_end_col_tl   .south~east) -- % noqa: S103
%    \end{macrocode}
% bottom right to bottom left corner of the last line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (0.5,-1.5) -- (-1,-1.5);
%   \node[fill=red,circle] at (0.5,-1.5) {5};
%   \node[fill=red,circle] at (-1,-1.5) {6};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - \tl_use:N \l_@@_end_col_tl         .south~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - 0                                  .south~west) -- % noqa: S103
%    \end{macrocode}
% top left corner of the highest fully captured line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (-1,-0.5) -- (0,-0.5);
%   \node[fill=red,circle] at (-1,-0.5) {7};
%   \node[fill=red,circle] at (0,-0.5) {8};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz-out- \tl_use:N \l_@@_middle_n_row_tl - 0                            .north~west) -- % noqa: S103
        (hexdumptikz-out- \tl_use:N \l_@@_middle_n_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103
        cycle
        ;
    \end{ scope }
  }
  \group_end:
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\l_@@_label_bytes_fallback:nnn}
% Enclose a sequence of bytes with a line -- fallback which also works without the lookup.
% Thus, this function can also be used once the mapping which was created by printing the hexdump is lost.
% Though, the output is not as nice as with that mapping available.
%
% This does not list sideeffects as there are many variables affected by this.
% On the other hand, the code is enclosed by a fresh group, so changes are local anyhow.
%
% \begin{args}
%   1 & \ain  & pgfkeys for the line \\
%   2 & \ain  & start-address \\
%   3 & \ain  & end-address \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_label_bytes_fallback:nnn #1 #2 #3
{
%    \end{macrocode}
% grouping needed to avoid pgfkeys leaking options
%    \begin{macrocode}
  \group_begin:
  \@@_label_bytes_init:nnn { #1 } { #2 } { #3 }
%    \end{macrocode}
%
% check if the annotation spans multiple rows/lines $\to$ maybe the drawing can be simplified
%    \begin{macrocode}
  \str_if_eq:VVTF
  \l_@@_start_row_tl
  \l_@@_end_row_tl
  {
    \begin{ scope }[/hexdumptikz/next~scope]
      \draw
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl   .north~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl   .south~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .south~west) -- % noqa: S103
        cycle
        ;
    \end{ scope }
  }
  {
%    \end{macrocode}
% this procedure does not rely on being able to translate addresses to row-indices
% Thus, this can serve as fallback if the prop required for the lookup is not available anymore
% Though, the spacing of the drawn border is not as nice as with the correct handling of the middle line

% draws the line beginning at the top left clockwise
%    \begin{macrocode}
    \begin{ scope }[/hexdumptikz/next~scope]
      \draw
%    \end{macrocode}
% top left corner to top right corner of the first line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (0,0) -- (1,0);
%   \node[fill=red,circle] at (0,0) {1};
%   \node[fill=red,circle] at (1,0) {2};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl -end                           .north~east) -- % noqa: S103
%    \end{macrocode}
% bottom right corner of the lowest fully captured line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (1,-1) -- (0.5,-1);
%   \node[fill=red,circle] at (1,-1) {3};
%   \node[fill=red,circle] at (0.5,-1) {4};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl -end.north~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - \tl_use:N \l_@@_end_col_tl     .north~east) -- % noqa: S103
%    \end{macrocode}
% bottom right to bottom left corner of the last line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (0.5,-1.5) -- (-1,-1.5);
%   \node[fill=red,circle] at (0.5,-1.5) {5};
%   \node[fill=red,circle] at (-1,-1.5) {6};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - \tl_use:N \l_@@_end_col_tl     .south~east) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - 0                              .south~west) -- % noqa: S103
%    \end{macrocode}
% top left corner of the highest fully captured line
% \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt]
%   \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle;
%   \draw[red] (-1,-0.5) -- (0,-0.5);
%   \node[fill=red,circle] at (-1,-0.5) {7};
%   \node[fill=red,circle] at (0,-0.5) {8};
% \end{tikzpicture}
%    \begin{macrocode}
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - 0                            .south~west) -- % noqa: S103
        (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .south~west) -- % noqa: S103
        cycle
        ;
    \end{ scope }
  }
  \group_end:
}
%    \end{macrocode}
% \end{fn}
% \iffalse
%</package>
% \fi
%
% \Finale
