%%%==============================================================================
%% Copyright 2023-present by Alceu Frigeri
%%
%% This work may be distributed and/or modified under the conditions of
%%
%% * The [LaTeX Project Public License](http://www.latex-project.org/lppl.txt),
%%   version 1.3c (or later), and/or
%% * The [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html),
%%   version 3 (or later)
%%
%% This work has the LPPL maintenance status *maintained*.
%%
%% The Current Maintainer of this work is Alceu Frigeri
%%
%% This is version {1.5} {2025/11/06}
%%
%% The list of files that compose this work can be found in the README.md file at
%% https://ctan.org/pkg/pgfkeysearch
%%
%%%==============================================================================
\NeedsTeXFormat{LaTeX2e}[2022/06/01]


\ProvidesExplPackage
    {pgfkeysearch}
    {2025/11/06}
    {1.5}
    {pgfkeys Search Extension}

\ExplSyntaxOn
%%%%%%%
%%%
%%% Just an attempt of having my packages info in a regular way
%%% \Pkginfograb_set:nn {<pkg-name>} { props} for each and all.
%%%
%%%%%%%
\RequirePackage{pkginfograb}
\pkginfograb_set:nn { pgfkeysearch }
  {
     name        = {pgfkeysearch} ,
     prefix      = {pgfkeysearch} ,
     date        = {2025/11/06},
     version     = {1.5} ,
     description = {pgfkeys~ search~ extension}
  }
%%%%%%%
%%% End of cut-n-paste
%%%%%%%


\msg_new:nnnn {pgfkeysearch} {deprecated}
  {
    (ID:#1)~The~ command~#2~is~ deprecated~ use~ #3 ~ instead
  }
  {
    You~tried~to~use~a~deprecated~ command:#2.
    Use~ #3~ instead.
    ~Error~Code~ ID:<#1>.
  }


\cs_set_eq:NN \__pgfkeysearch_exp_arg:NNw \exp_args:NNv
\cs_set_eq:NN \__pgfkeysearch_tl_clear:N \tl_clear:N
\cs_set_eq:NN \__pgfkeysearch_if_root_search:Nn \use_none:nn

\keys_define:nn { pgfkeysearch }
{  
  root~ search  .usage:n = general ,
  root~ search  .choices:nn = 
    { {} , true , false  } 
    { 
      \str_if_eq:VnTF \l_keys_choice_str {false}
        { \cs_set_eq:NN \__pgfkeysearch_if_root_search:Nn \use_none:nn }
        { \cs_set_eq:NN \__pgfkeysearch_if_root_search:Nn \seq_put_right:Nn  }
    }  ,

  key .usage:n = general,
	key .choice:,
  key / value .code:n = { \cs_set_eq:NN \__pgfkeysearch_exp_arg:NNw \exp_args:NNv } ,
  key / macro .code:n = { \cs_set_eq:NN \__pgfkeysearch_exp_arg:NNw \exp_args:NNc } ,
  key .value_required:n = true ,

  settings .usage:n = general,
	settings .choice:,
  settings / old .code:n = 
    {
      \cs_set_eq:NN \__pgfkeysearch_tl_clear:N \use_none:n

      \cs_set_eq:NN \pgfkeysearchvalueof    \__pgfkeysearch_old_valueof:nnN
      \cs_set_eq:NN \pgfkeysearchvalueofTF  \__pgfkeysearch_old_valueofTF:nnNTF
      \cs_set_eq:NN \pgfkeygetvalueof       \__pgfkeysearch_old_getvalueof:nnN
      \cs_set_eq:NN \pgfkeygetvalueofTF     \__pgfkeysearch_old_getvalueofTF:nnNTF
      \cs_set_eq:NN \pgfkeysearch           \pgfkeysearchvalueof
      \cs_set_eq:NN \pgfkeysearchTF         \pgfkeysearchvalueofTF
      \cs_set_eq:NN \pgfkeyget              \pgfkeygetvalueof
      \cs_set_eq:NN \pgfkeygetTF            \pgfkeygetvalueofTF
    } ,
  settings / new .code:n = 
    {
      \cs_set_eq:NN \__pgfkeysearch_tl_clear:N \tl_clear:N

      \cs_set_eq:NN \pgfkeysearchvalueof    \pgfkeysearch_multipath_keysearch:nnN
      \cs_set_eq:NN \pgfkeysearchvalueofTF  \pgfkeysearch_multipath_keysearch:nnNTF
      \cs_set_eq:NN \pgfkeygetvalueof       \pgfkeysearch_keyget:nnN
      \cs_set_eq:NN \pgfkeygetvalueofTF     \pgfkeysearch_keyget:nnNTF
      \cs_set_eq:NN \pgfkeysearch           \pgfkeysearchvalueof
      \cs_set_eq:NN \pgfkeysearchTF         \pgfkeysearchvalueofTF
      \cs_set_eq:NN \pgfkeyget              \pgfkeygetvalueof
      \cs_set_eq:NN \pgfkeygetTF            \pgfkeygetvalueofTF
    } ,
	settings .value_required:n = true ,
}

  
\cs_new_protected:Npn \pgfkeysearch_settings:n #1
  { 
    \keys_set:nn {pgfkeysearch}{#1} 
  } 

%%%%%%%%%%%%%%%
%%%
%%% _keysearch allows to find a key, 
%%% if it's defined 'anywhere' in the tree. I mean, upwards until, likely, /some/<key>
%%% if it isn't defined, returns nothing (no error triggered)
%%%
%%%%%%%%%%%%%%%
\seq_new:N \l__pgfkeysearch_pathterms_seq
\seq_new:N \l__pgfkeysearch_pathtree_seq
\tl_new:N  \l__pgfkeysearch_path_tl

\cs_generate_variant:Nn \tl_set:Nn {Ne}


%%%%%%%%%%%%%%%
%%%
%%% This will create a pair
%%%                  *none* expandable
%%% #cmd:signature#    => cs
%%% #cmd:signature#TF  => prg
%%%
%%%%%%%%%
%%%
%%% #1 -> #cmd:signature#           => base-name
%%% #2 -> base-code                 
%%%
%%%%%%%%%%%%%%%
\regex_const:Nn \c__pgfkeysearch_rtn_regex {\c{__pgfkeysearch_set_rtn_true:}|\c{__pgfkeysearch_set_rtn_false:}}
\cs_new_protected:Npn \__pgfkeysearch_duo_new:Nn #1#2
  {
    \tl_set:Nn \l__pgfkeysearch_tmpa_tl {#2}
    \regex_replace_all:NnN \c__pgfkeysearch_rtn_regex {} \l__pgfkeysearch_tmpa_tl
    \__pgfkeysearch_duo_aux:NVn #1 \l__pgfkeysearch_tmpa_tl {#2} 
  }

\cs_new_protected:Npn \__pgfkeysearch_duo_aux:Nnn #1#2#3
  {
    \cs_new_protected:Nn #1
      { #2 }
    \prg_new_protected_conditional:Nnn #1 {T , F , TF}
      {
        #3
        \__pgfkeysearch_return:
      }
  }
\cs_generate_variant:Nn   \__pgfkeysearch_duo_aux:Nnn {NV}


%%%%%%%%%%%%%%%
%%% so that they can be easily removed (if needed) by _duo_new:Nn
%%%%%%%%%%%%%%%
\cs_new:Npn \__pgfkeysearch_set_rtn_true:
  { \cs_set_eq:NN \__pgfkeysearch_return: \prg_return_true: }
\cs_new:Npn \__pgfkeysearch_set_rtn_false:
  { \cs_set_eq:NN \__pgfkeysearch_return: \prg_return_false: }

%%%%%%%%%%%%%%%
%%%
%%% This is, basicaly, \pgfkeysvalueof
%%% but, assigning the found value (if any) to #3
%%%
%%%%%%%%%%%%%%%
\__pgfkeysearch_duo_new:Nn \pgfkeysearch_keyget:nnN
  {
    \cs_if_exist:cTF {pgfk@#1/#2}
      {
        \__pgfkeysearch_exp_arg:NNw \tl_set:Nn #3 {pgfk@#1/#2}            
        \__pgfkeysearch_set_rtn_true:
      }
      {
        \__pgfkeysearch_tl_clear:N #3
        \__pgfkeysearch_set_rtn_false:
      }
  }

%%%%%%%%%%%%%%%
%%%
%%% Given a single path /A/B/C/D it will look after
%%%  /A/B/C/D/<key> 
%%%  /A/B/C/<key>
%%%  /A/B/<key>
%%%  /A/<key>
%%%  /<key> (if "root search" set)
%%%    stoping at the first hit
%%%
%%%%%%%%%%%%%%%
\__pgfkeysearch_duo_new:Nn \pgfkeysearch_keysearch:nnN
  {
    \seq_set_split:Nne \l__pgfkeysearch_pathterms_seq {/} {#1}
    \seq_remove_all:Nn \l__pgfkeysearch_pathterms_seq {}
    \tl_clear:N \l__pgfkeysearch_path_tl
    \seq_clear:N \l__pgfkeysearch_pathtree_seq

    \__pgfkeysearch_if_root_search:Nn
       \l__pgfkeysearch_pathtree_seq {}

    \seq_map_inline:Nn \l__pgfkeysearch_pathterms_seq
      {
        \tl_put_right:Nn \l__pgfkeysearch_path_tl {/##1}
        \seq_put_right:NV \l__pgfkeysearch_pathtree_seq \l__pgfkeysearch_path_tl
      }
    \seq_reverse:N \l__pgfkeysearch_pathtree_seq

    \__pgfkeysearch_set_rtn_false:
    \__pgfkeysearch_tl_clear:N #3
    
    \seq_map_inline:Nn \l__pgfkeysearch_pathtree_seq
      {
        \cs_if_exist:cTF {pgfk@##1/#2}
          {
            \__pgfkeysearch_exp_arg:NNw \tl_set:Nn #3 {pgfk@##1/#2}            
            \__pgfkeysearch_set_rtn_true:
            \seq_map_break:
          }
          { }
      }
  }

 
\cs_new_protected:Npn \pgfkeysearch_keysearch:nnnTF
  {
    \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnnTF}{\cs_to_str:N \pgfkeysearch_keysearch:nnNTF}
    \pgfkeysearch_keysearch:nnNTF
  }
\cs_new_protected:Npn \pgfkeysearch_keysearch:nnnT
  {
    \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnnT}{\cs_to_str:N \pgfkeysearch_keysearch:nnNT}
    \pgfkeysearch_keysearch:nnNT
  }
\cs_new_protected:Npn \pgfkeysearch_keysearch:nnnF
  {
    \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnnF}{\cs_to_str:N \pgfkeysearch_keysearch:nnNF}
    \pgfkeysearch_keysearch:nnNF
  }

%%%%%%%%%%%%%%%
%%%
%%% #1 shall be a comma separated list of paths (can be a single one)
%%% it searchs for a key in every path, stoping at the first hit.
%%%
%%%%%%%%%%%%%%%
\__pgfkeysearch_duo_new:Nn \pgfkeysearch_multipath_keysearch:nnN
  {
    \seq_set_from_clist:Nn \l__pgfkeysearch_tmpa_seq {#1}
    
    \seq_map_inline:Nn \l__pgfkeysearch_tmpa_seq
      {
        \pgfkeysearch_keysearch:nnNTF {##1}{#2} #3
          { \seq_map_break: }
          {}
      }
  }
 
\cs_new_protected:Npn \pgfkeysearch_multipath_keysearch:nnnTF
  {
    \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnnTF}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnNTF}
    \pgfkeysearch_multipath_keysearch:nnNTF
  }
\cs_new_protected:Npn \pgfkeysearch_multipath_keysearch:nnnT
  {
    \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnnT}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnNT}
    \pgfkeysearch_multipath_keysearch:nnNT
  }
\cs_new_protected:Npn \pgfkeysearch_multipath_keysearch:nnnF
  {
    \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnnF}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnNF}
    \pgfkeysearch_multipath_keysearch:nnNF
  }


%%%%%%%%%%%%%%%
%%%
%%% The caveats: none of this is expandable.
%%%
%%%%%%%%%%%%%%%
%%%
%%% This is the more generic one. 
%%% #1 is the path (or list of paths)
%%% #2 is the <key>
%%% #3 is the macro that will receive the key value (if any)
%%%
%%%%%%%%%%%%%%%

%\NewDocumentCommand{\pgfkeysearchsettings}{m}
%  { \pgfkeysearch_settings:n {#1} }
\cs_new_eq:NN \pgfkeysearchsettings \pgfkeysearch_settings:n

\cs_new:Npn \__pgfkeysearch_old_valueof:nnN #1#2#3
  {
        \pgfkeysearch_multipath_keysearch:nnNTF {#1}{#2}#3
          { }
          { \tl_clear:N #3 }    
  }
\cs_new:Npn \__pgfkeysearch_old_valueofTF:nnNTF #1#2#3#4#5
  {
    \pgfkeysearch_multipath_keysearch:nnNTF {#1}{#2}#3
      { #4 }
      { 
        \tl_clear:N #3
        #5
      }
  }
\cs_new:Npn \__pgfkeysearch_old_getvalueof:nnN #1#2#3
  {
    \pgfkeysearch_keyget:nnNTF {#1}{#2}#3
      { }
      { \tl_clear:N #3 }
  }
\cs_new:Npn \__pgfkeysearch_old_getvalueofTF:nnNTF #1#2#3#4#5
  {
    \pgfkeysearch_keyget:nnNTF {#1}{#2}#3
      { #4 }
      { 
        \tl_clear:N #3
        #5
      }
  }
  
\keys_set:nn {pgfkeysearch}{settings=new}  

\ProcessKeyOptions [pgfkeysearch] 

