% \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-addrcalc.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_addrcalc>
% \fi
%
% \maketitle
%
% \begin{abstract}
%   Defines helpers and variables which are use throughout the whole project.
% \end{abstract}
%
% Identify the package and give the over all version information.
%    \begin{macrocode}
\ProvidesExplPackage {hexdumptikz-addrcalc} {2026-06-16} {1.0.0}
  {Functionality to work / calculate (limited) with larger addresses}

\RequirePackage { hexdumptikz-common }
%    \end{macrocode}
%
% \subsection{Helpers}
%
% \begin{fn}{\@@_byte_index:n}
% Leaves the index (column) of a byte on the output
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & lower digits of an address \\
%   - & \ain & \texttt{l\_hexdumptikz\_common\_bytes\_per\_row\_int} \\
% \end{args}
%    \begin{macrocode}
\cs_new:Npn \@@_byte_index:n #1
{
  \int_mod:nn
  { \int_from_hex:f { #1 } }
  { \l_hexdumptikz_common_bytes_per_row_int }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_row_lower:n}
% Leaves the lower part of the address (\enquote{y-coord}) on the output.
% Truncates the input address.
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & lower digits of an address \\
%   - & \ain & \texttt{l\_hexdumptikz\_common\_bytes\_per\_row\_int} \\
% \end{args}
%    \begin{macrocode}
\cs_new:Npn \@@_row_lower:n #1
{
  \int_to_hex:n
  {
    \int_div_truncate:nn
    { \int_from_hex:f { #1 } }
    { \l_hexdumptikz_common_bytes_per_row_int }
    * \l_hexdumptikz_common_bytes_per_row_int
  }
}
%    \end{macrocode}
% \end{fn}
%
% \subsection{pgfkeys}
% Define pgfkeys with is concerned with address calculations
%
% \begin{var}{\l_@@_hex_digits_calc_int}
% size of the address-suffix which is required for calculations
%    \begin{macrocode}
\int_new:N \l_@@_hex_digits_calc_int
%    \end{macrocode}
% \end{var}
%
%    \begin{macrocode}
\pgfkeys {
  /hexdumptikz,
  % amount of bytes in a row -> needs to be enforced during parsing
  bytes~per~row/.code = {
    \int_set:Nn \l_hexdumptikz_common_bytes_per_row_int { \int_abs:n { #1 } }
    \int_set:Nn \l_@@_hex_digits_calc_int
      % 16 because addresses must be hexadecimal
      { ( \l_hexdumptikz_common_bytes_per_row_int + 16 - 1 ) / 16 }
    \int_compare:nNnT
    { \l_@@_hex_digits_calc_int }
    >
    { 30 }
    {
      \msg_warn:nnV
      { hexdumptikz }
      { too-many-digits }
      \l_@@_hex_digits_calc_int
    }
    \int_case:nnF { \l_hexdumptikz_common_bytes_per_row_int }
    {
      { 1 } { }
      { 2 } { }
      { 4 } { }
      { 8 } { }
      { 16 } { }
    }
    {
      \int_compare:nNnF
      { \int_mod:nn { \l_hexdumptikz_common_bytes_per_row_int } { 16 } }
      =
      { 0 }
      {
        \msg_warning:nnV
        { hexdumptikz }
        { weird-num-bytes-per-row }
        \l_hexdumptikz_common_bytes_per_row_int
      }
    }
  },
  bytes~per~row = { 8 },
  bytes~per~row/.value~required,

  % amount of digits the addresses are padded to (without the leading 0x)
  addr~len/.code = {
    \int_set:Nn \l_hexdumptikz_common_addr_len_int { \int_abs:n { #1 } }
  },
  addr~len = { 12 },
  addr~len/.value~required,
}
%    \end{macrocode}
%
% \subsection{Variables}
% Define variables which are used in the calculation itself.
% Note these variables are not used for configuration!
%
% \begin{var}{\l_@@_addr_tl}
% \begin{var}{\l_@@_addr_prefix_tl}
% \begin{var}{\l_@@_addr_suffix_tl}
% \begin{var}{\l_@@_row_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_addr_tl
\tl_new:N \l_@@_addr_prefix_tl
\tl_new:N \l_@@_addr_suffix_tl
\tl_new:N \l_@@_row_tl
%    \end{macrocode}
% \end{var}
% \end{var}
% \end{var}
% \end{var}
%
% \begin{var}{\l_@@_addr_len_int}
% \begin{var}{\l_@@_addr_prefix_len_int}
% \begin{var}{\l_@@_byte_index_int}
%    \begin{macrocode}
\int_new:N \l_@@_addr_len_int % used in calculation != configuration option
\int_new:N \l_@@_addr_prefix_len_int
\int_new:N \l_@@_byte_index_int
%    \end{macrocode}
% \end{var}
% \end{var}
% \end{var}
%
% \subsection{Functions}
%
% \begin{fn}{\hexdumptikz_address_to_nodename_components:nNN}
% Function which splits an address into its two components (row/offset and column) as it corresponds to the hexdump.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_@@\_addr\_tl} \\
%   \sclobber & \sdir & \texttt{l\_@@\_addr\_len\_int} \\
%   \sclobber & \sdir & \texttt{l\_@@\_addr\_prefix\_len\_int} \\
%   \sclobber & \sdir & \texttt{l\_@@\_addr\_prefix\_tl} \\
%   \sclobber & \sdir & \texttt{l\_@@\_byte\_index\_int} \\
%   \sclobber & \sdir & \texttt{l\_@@\_addr\_suffix\_tl} \\
%   \sclobber & \sdir & \texttt{l\_@@\_row\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl address to work on \\
%   2 & \aout & output macro for the y-component of the coordinate (tl) \\
%   3 & \aout & output macro for the x-component of the coordinate (tl) \\
%   - & \ain  & \texttt{l\_@@\_hex\_digits\_calc\_int} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \hexdumptikz_address_to_nodename_components:nNN #1 #2 #3
{
%    \end{macrocode}
% Make a working copy of the address
%    \begin{macrocode}
  \tl_set:Ne \l_@@_addr_tl { #1 }
%    \end{macrocode}
%
% validate address
%    \begin{macrocode}
  \regex_if_match:NVF
  \c_hexdumptikz_common_hex_regex
  \l_@@_addr_tl
    {
      \msg_critical:nnV
        { hexdumptikz }
        { invalid-address }
        \l_@@_addr_tl
    }
%    \end{macrocode}
%
% Padd the address to avoid issues regarding the indices
%    \begin{macrocode}
  \hexdumptikz_common_pad_address:N \l_@@_addr_tl
%    \end{macrocode}
%
% strip the leading \texttt{0x}
%    \begin{macrocode}
  \regex_replace_once:NnN
  \c_hexdumptikz_common_leading_hex_base_regex
  { }
  \l_@@_addr_tl
%    \end{macrocode}
%
% obtain the total length (might be longer than to what was padded)
%    \begin{macrocode}
  \int_set:Nn \l_@@_addr_len_int
    { \tl_count:N \l_@@_addr_tl }
%    \end{macrocode}
%
% check length for validity to avoid issues later (should never happen due to the padding)
%    \begin{macrocode}
  \int_compare:nNnT
    { \l_@@_addr_len_int }
    <
    { \l_@@_hex_digits_calc_int }
    {
      \msg_critical:nnVV
        { hexdumptikz }
        { address-too-short }
        \l_@@_addr_tl
        \l_@@_hex_digits_calc_int
    }
%    \end{macrocode}
%
% calculate the length the prefix should have
%    \begin{macrocode}
  \int_set:Nn \l_@@_addr_prefix_len_int
    {
      \l_@@_addr_len_int
      -
      \l_@@_hex_digits_calc_int
    }
%    \end{macrocode}
%
% extract the prefix
%    \begin{macrocode}
  \tl_set:Ne \l_@@_addr_prefix_tl
    {
      \tl_range:Nnn \l_@@_addr_tl
      { 1 }
      { \l_@@_addr_prefix_len_int }
    }
%    \end{macrocode}
%
% extract the suffix
%    \begin{macrocode}
  \tl_set:Ne \l_@@_addr_suffix_tl
  {
    \tl_range:Nnn \l_@@_addr_tl
    { \l_@@_addr_prefix_len_int + 1 }
    { \l_@@_addr_len_int }
  }
%    \end{macrocode}
%
% obtain the x-coordinate and (part of) the y-coordinate based on the suffix
%    \begin{macrocode}
  \int_set:Nn \l_@@_byte_index_int
    {
      \@@_byte_index:n
      { \l_@@_addr_suffix_tl }
    }
  \tl_set:Ne \l_@@_row_tl
    {
      \@@_row_lower:n
      { \l_@@_addr_suffix_tl }
    }
%    \end{macrocode}
%
% padd the y-coordinate suffix to avoid issues when concatenating with the prefix
%    \begin{macrocode}
  \hexdumptikz_common_pad_left:Nnn
    \l_@@_row_tl
    { \l_@@_hex_digits_calc_int }
    { 0 }
%    \end{macrocode}
%
% Store the two coord components in the output macros
%    \begin{macrocode}
  \tl_set:Ne #2
    {
      \l_@@_addr_prefix_tl
      \l_@@_row_tl
    }
  \tl_set:Ne #3
    {
      \int_to_arabic:n { \l_@@_byte_index_int }
    }
}
\cs_generate_variant:Nn \hexdumptikz_address_to_nodename_components:nNN { eNN }
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_address_to_nodename:n}
% Convert a full address to its nodename as used in the printed hexdump (leaves the nodename on the output)
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl address to work on \\
% \end{args}
%    \begin{macrocode}
\cs_new:Npn \@@_address_to_nodename:n #1
{
  \hexdumptikz_address_to_nodename_components:nNN
  { #1 }
  \l_tmpa_tl
  \l_tmpb_tl

  \l_tmpa_tl
  \l_tmpb_tl
}
\cs_generate_variant:Nn \@@_address_to_nodename:n { e }
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_address_to_row:n}
% Convert a full address to its row/address-component (leaves the row/address-component on the output)
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl address to work on \\
% \end{args}
%    \begin{macrocode}
\cs_new:Npn \@@_address_to_row:n #1
{
  \hexdumptikz_address_to_nodename_components:nNN
  { #1 }
  \l_tmpa_tl
  \l_tmpb_tl

  \l_tmpa_tl
}
\cs_generate_variant:Nn \@@_address_to_row:n { e }
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_address_to_col:n}
% Convert a full address to its column-component (leaves the column-component on the output)
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl address to work on \\
% \end{args}
%    \begin{macrocode}
\cs_new:Npn \@@_address_to_col:n #1
{
  \hexdumptikz_address_to_nodename_components:nNN
  { #1 }
  \l_tmpa_tl
  \l_tmpb_tl

  \l_tmpb_tl
}
\cs_generate_variant:Nn \@@_address_to_col:n { e }
%    \end{macrocode}
% \end{fn}
%
% \iffalse
%</package>
% \fi
%
% \Finale
