% \iffalse meta-comment
%
%% File: latex-lab-toc.dtx (C) Copyright 2022-2024 LaTeX Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
%
% The development version of the bundle can be found below
%
%    https://github.com/latex3/latex2e/required/latex-lab
%
% for those people who are interested or want to report an issue.
%
\def\ltlabtocdate{2024-07-11}
\def\ltlabtocversion{0.85c}
%<*driver>
\documentclass{l3doc}
\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{latex-lab-toc.dtx}
\end{document}
%</driver>
%
% \fi
%
%
% \title{The \textsf{latex-lab-toc} package\\
% Changes related to the tagging of toc and similar lists}
% \author{\LaTeX{} Project\thanks{Initial implementation done by Ulrike Fischer}}
% \date{v\ltlabtocdate\ \ltlabtocversion}
%
% \maketitle
%
% \newcommand{\xt}[1]{\textsl{\textsf{#1}}}
% \newcommand{\TODO}[1]{\textbf{[TODO:} #1\textbf{]}}
% \newcommand{\docclass}{document class \marginpar{\raggedright document class
% customizations}}
%
%
% \ProvideDocElement[printtype=\textit{socket},idxtype=socket,idxgroup=Sockets]{Socket}{socketdecl}
% \ProvideDocElement[printtype=\textit{hook},idxtype=hook,idxgroup=Hooks]{Hook}{hookdecl}
% \ProvideDocElement[printtype=\textit{plug},idxtype=plug,idxgroup=Plugs]{Plug}{plugdecl} 
%
% \begin{abstract}
% \end{abstract}
% 
% Header for the testphase package
%    \begin{macrocode}
%<*header>
\ProvidesExplPackage {latex-lab-testphase-toc} {\ltlabtocdate} {\ltlabtocversion}
  { Code related to the tagging of toc-like lists}
%</header>  
%    \end{macrocode}
% \begin{documentation}
% \section{Introduction}
%
% The followings contains various functions related to the tagging of the
% table of contents and similar list. 
% 
% The structure of tocs consist of nested TOC and TOCI structures. 
% The code uses the first argument of the \cs{contentsline} command to detect the
% level and to decided if a structure should be closed. The structure that 
% should be used in \texttt{/Ref} key to link to the heading is detected from the
% target name in the fourth argument -- because of this with this code such a target 
% name is created and stored also if hyperref is not loaded. 
% 
% \subsection{Manual toc additions}
% As the \texttt{/Ref} key relies on the target name, 
% manual \cs{addcontentsline} commands must ensure that they reference the right 
% structure. If an unnumbered heading command is used before this is normally the case, 
% so the following should work fine:
% 
%  \begin{verbatim}
%  \chapter*{Unnumbered}
%  \addcontentsline{toc}{chapter}{Unnumbered}
%  \end{verbatim}
%  
%  If there is no heading command a target name must be created manually \emph{inside the
%  right structure} with \cs{MakeLinkTarget}:
%  
%  \begin{verbatim}
%  \noindent % start e.g. P-structure
%  \MakeLinkTarget*{unnumbered}% target inside the P-structure
%  Unnumbered
%  \addcontentsline{toc}{chapter}{Unnumbered}
%  \end{verbatim}   
% 
% \end{documentation}
% \begin{implementation}
%    \begin{macrocode}
%<*package>
%<@@=tag>
%    \end{macrocode}
% \section{Temporary variables}
% \begin{macro}{\l_@@_toc_tmpa_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_toc_tmpa_tl 
%    \end{macrocode}
% \end{macro}
%
% \section{General struct commands}
% The following variables and commands are not restricted to toc, but
% probably will be need in other places too.
% \begin{variable}{\g_@@_struct_dest_num_prop}
% This variable records for (some or all, not clear yet)
% destination names the related structure number to allow
% to reference them in a Ref. The key is the destination.
% Defined by tagpdf.
% \end{variable}
%
% \cs{refstepcounter} doesn't use \cs{MakeLinkTarget} (yet) so
% we have to patch it too to store the relation between
% destination names/\cs{@currentHref} and structure numbers
%
% The property is set up in tagpdf-test so that one
% doesn't has to check if the prop exists or not.
%    \begin{macrocode}
\AddToHook{cmd/refstepcounter/after}
 {
   \tl_if_blank:VF \@currentHref
    {
      \prop_gput:Nee \g_@@_struct_dest_num_prop {\@currentHref}{\tag_get:n{struct_num}}      
    }
 }
\AddToHook{cmd/H@refstepcounter/after}
 {
   \tl_if_blank:VF \@currentHref
    {
      \prop_gput:Nee \g_@@_struct_dest_num_prop {\@currentHref}{\tag_get:n{struct_num}}
    }
 }
%    \end{macrocode}
% \begin{variable}{\g_@@_struct_ref_by_dest_prop}
% This variable contains structures whose Ref key should be updated
% at the end to point to structured related with this destination.
% As this is probably need in other places too, it is not only a toc-variable.
% Moved into tagpdf!
% \end{variable}
%
% \begin{macro}{\g_@@_struct_ref_by_dest:}
%  This command is executed and update the Ref keys
%  of the structures listed in |\g_@@_struct_ref_by_dest_prop|.
%  It is currently only relevant for the |TOCI|. But other structures
%  could need that later too.
%  The command is executed in the |tagpdf/finish/before| hook.
%    \begin{macrocode}
\msg_new:nnn { tag } {struct-dest-unknown}
 {
   Destination~#1~has~no~related~structure.\\
   /Ref~for~structure~#2~not~updated
 }

\cs_new_protected:Npn \g_@@_struct_ref_by_dest:
  {
    \prop_map_inline:Nn\g_@@_struct_ref_by_dest_prop
      {
        \prop_get:NnNTF \g_@@_struct_dest_num_prop {##2} \l_@@_tmpa_tl
          {
            \@@_struct_gput_data_ref:ee
              { ##1 }
              { \tag_struct_object_ref:e{ \l_@@_tmpa_tl }}
          }
          {
            \msg_warning:nnnn {tag}{struct-dest-unknown}{##2}{ ##1}
          }
      }
  }
\hook_gput_code:nnn {tagpdf/finish/before}{tagpdf/struct/Ref}{\g_@@_struct_ref_by_dest:}
%    \end{macrocode}
% \end{macro}
%
% \section{Toc code}
% \begin{variable}{\g_@@_toc_level_int,\g_@@_toc_stack_seq}
% |\g_@@_toc_level_int| records in a toc the current absolute level.
% We must close open structures at the end of the toc, for this
% we maintain a stack |\g_@@_toc_stack_seq|.
%    \begin{macrocode}
\int_new:N \g_@@_toc_level_int
\seq_new:N \g_@@_toc_stack_seq
%    \end{macrocode}
% \end{variable}

% \begin{macro}{\_tag_toc_starttoc_init:n}
% The init code clears the stack, and set the level to -100
% and start to TOC structure. We also disable paratagging.
% The |/Title| is currently simply the type, but this could be done nicer.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_starttoc_init:n #1
 {
    \bool_set_false:N \l_@@_para_bool
    \seq_gclear:N \g_@@_toc_stack_seq
    \int_gset:Nn  \g_@@_toc_level_int {-100}
    \tag_struct_begin:n{tag=TOC,title=#1}
 }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/toc/starttoc/before}{default}
 {
   \@@_toc_starttoc_init:n{#1}
 }
\AssignSocketPlug{tagsupport/toc/starttoc/before}{default}
%    \end{macrocode}
% \end{plugdecl}
% \begin{macro}{\@@_toc_starttoc_finalize:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_starttoc_finalize:
 {
    \int_step_inline:nn
      {\seq_count:N \g_@@_toc_stack_seq }
      {\tag_struct_end:}
    \tag_struct_end:
    \seq_gclear:N \g_@@_toc_stack_seq
 }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/toc/starttoc/after}{default}
 {
   \@@_toc_starttoc_finalize:
 }
\AssignSocketPlug{tagsupport/toc/starttoc/after}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{macro}{\@@_toc_end:n}
% This commands ends all TOC on the stack with a level higher than the argument
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_end:n #1 
 {
   \seq_get:NNT\g_@@_toc_stack_seq \l_@@_toc_tmpa_tl 
     {
       \bool_lazy_and:nnT
         {
           \str_if_eq_p:ee{\tl_head:N\l_@@_toc_tmpa_tl}{TOC}
         }
         {
           \int_compare_p:nNn {#1}<{\tl_tail:N \l_@@_toc_tmpa_tl}
         }
         {
           \seq_gpop:NN\g_@@_toc_stack_seq \l_@@_toc_tmpa_tl 
           \tag_struct_end:
           \@@_toc_end:n{#1}
         }
     }
 }
\cs_generate_variant:Nn \@@_toc_end:n {e} 
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_toc_contentsline_begin:nnnn}
% This is main command executed at the begin of a |\contentsline|.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_contentsline_begin:nnnn #1 #2 #3 #4 
  %#1 level, #2 content, #3 page number (unused) #4 destination
  {
   \tag_if_active:T
     {
%    \end{macrocode}
% We detect the intended level by checking the value of |toclevel@...|
% (currently only provided by hyperref, but should be there always).
% To be on the safe side we set it to 1 if not defined.
%    \begin{macrocode}
       \ExpandArgs{c}\providecommand { toclevel@#1 }{ 1 } %  just in case ...
       \int_compare:nNnF { \use:c{toclevel@#1} } > {\use:c{c@tocdepth}}
        {
%    \end{macrocode}
% if level goes up, start new sub TOC unless we are at the begin
%    \begin{macrocode}
          \bool_lazy_and:nnT
            { \int_compare_p:nNn { \g_@@_toc_level_int } > {-100} }
            { \int_compare_p:nNn { \use:c{toclevel@#1} }   > { \g_@@_toc_level_int } }
            {
              \seq_gpush:Ne \g_@@_toc_stack_seq {{TOC}\use:c{toclevel@#1}}
              \tag_struct_begin:n{tag=TOC}
            }
%    \end{macrocode}
% if level goes down close all TOC's with a higher level
%    \begin{macrocode}
          \int_compare:nNnT
            { \use:c{toclevel@#1} } < { \g_@@_toc_level_int }
            {
              \@@_toc_end:e { \use:c{toclevel@#1} }
            }
%    \end{macrocode}
%if same level do nothing
% update toclevel to the current level.
%    \begin{macrocode}
          \int_gset:Nn \g_@@_toc_level_int { \use:c{toclevel@#1} }
%    \end{macrocode}
% now open the TOCI, the tagging of the
% inner structure is left to the |\l@xxx| commands.
% setting the title is not strictly necessary but looks nicer
% but we have to remove the |\numberline|
% \begin{NOTE}{UF}
% perhaps keep the number? How to insert a space then
% \end{NOTE}
%    \begin{macrocode}
          \group_begin:
           \text_declare_expand_equivalent:Nn \numberline \use_none:n
           \exp_args:Ne \tag_struct_begin:n{tag=TOCI,title={\text_purify:n {#2}}}
%    \end{macrocode}
% The TOCI structure should get a /Ref, so we put a request with its destination
% name into the prop.
% \begin{NOTE}{UF}
% This only works with hyperref currently. Without hyperref we
% need to store fake names.
% \end{NOTE}
%    \begin{macrocode}
           \prop_gput:Nee \g_@@_struct_ref_by_dest_prop
             { \tag_get:n {struct_num} }{#4}
           \seq_gpush:Ne \g_@@_toc_stack_seq {{TOCI}\use:c{toclevel@#1}}
          \group_end:
       }
    }
  }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/toc/contentsline/before}{default}
  {
    \@@_toc_contentsline_begin:nnnn #1
  }  
\AssignSocketPlug{tagsupport/toc/contentsline/before}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{macro}{\@@_toc_contentsline_end:nnnn}
% This is the closing code of a |\contentsline|.
% If the contentsline was actually printed, the code has to
% close the TOCI structure and to update the stack.
%    \begin{macrocode}
\msg_new:nnn {tag}{toc-no-TOCI}{Missing~TOCI~structure~on~toc~stack}

\cs_new_protected:Npn \@@_toc_contentsline_end:nnnn #1 #2 #3 #4 
% #1 level, #2 content (unused), #3 page number (unused) #4 destination (unused)
  {
    \int_compare:nNnF { \use:c{toclevel@#1} } > {\use:c{c@tocdepth}}
      {
        \seq_gpop:NNT \g_@@_toc_stack_seq\l_@@_tmpa_tl
          {
            \str_if_eq:eeTF{\tl_head:N\l_@@_tmpa_tl}{TOCI}
             {
               \tag_struct_end:
             }
             {
               \msg_warning:nn{tag}{toc-no-TOCI}
             }  
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default} 
%    \begin{macrocode}
\NewSocketPlug{tagsupport/toc/contentsline/after}{default}
  {
    \@@_toc_contentsline_end:nnnn #1
  }  
\AssignSocketPlug{tagsupport/toc/contentsline/after}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \subsection{Tagging of the content}
% This need discussion.
%
%    \begin{macrocode}
\AddToHook{contentsline/text/before}[tagpdf]{%
  \tag_struct_begin:n{tag=Reference}%
  \tag_mc_begin:n{tag=Reference}}
\AddToHook{contentsline/text/after}[tagpdf]{%
  \tag_mc_end:}
\AddToHook{contentsline/page/before}[tagpdf]{%
  \tag_mc_begin:n{tag=Reference}}
\AddToHook{contentsline/page/after}[tagpdf]{%
  \tag_mc_end:
  \tag_struct_end:} %Reference
\AddToHook{contentsline/number/before}[tagpdf]{%
  \tag_mc_end:
  \tag_struct_begin:n{tag=Lbl}%
  \tag_mc_begin:n{tag=Lbl}}
\AddToHook{contentsline/number/after}[tagpdf]{%
  \tag_mc_end:
  \tag_struct_end:
  \tag_mc_begin:n{tag=Reference}}
%    \end{macrocode}
% \begin{plugdecl}{artifact}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/toc/leaders/before}{artifact}
 {\tag_mc_begin:n{artifact}\nobreak}
\NewSocketPlug{tagsupport/toc/leaders/after}{artifact}
 {\nobreak\tag_mc_end:}
\AssignSocketPlug{tagsupport/toc/leaders/before}{artifact}
\AssignSocketPlug{tagsupport/toc/leaders/after}{artifact}
%    \end{macrocode}
% \end{plugdecl}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
% Wrapper for the testphase key.
%    \begin{macrocode}
%<*latex-lab>
\ProvidesFile{toc-latex-lab-testphase.ltx}
        [\ltlabtocdate\space v\ltlabtocversion latex-lab wrapper toc]

\RequirePackage{latex-lab-testphase-toc}

%</latex-lab>
%    \end{macrocode}
% \end{implementation}
