%%%==============================================================================
%% 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.2} {2025/01/09}
%%
%% The list of files that compose this work can be found in the README.md file at
%% https://ctan.org/pkg/tikzquests
%%
%%%==============================================================================
%% WARNING: These are personal packs/tests
%%          They might and probably will change at will as needed
%% 
%%%==============================================================================
\NeedsTeXFormat{LaTeX2e}[2022/06/01]


\ProvidesExplPackage
    {tikzquests}
    {2025/01/09}
    {1.2}
    {A Simple Framework for (tikz/text) Parametric Questions}

\ExplSyntaxOn
%%%%%%%
%%%
%%% Just an attempt of having my packages info in a regular way
%%% Idea being: { <pck-name> / pkg info } for each and all.
%%%
%%%%%%%
\keys_define:nn { tikzquests / pkg info}
  {
     name        .code:n = {tikzquests} ,
     prefix      .code:n = {tikzquests} ,
     date        .code:n = {2025/01/09},
     version     .code:n = {1.2} ,
     description .code:n = {A Simple Framework for (tikz/text) Parametric Questions}
  }
\cs_if_exist:NF \PkgInfo 
  {
    \NewDocumentCommand \PkgInfo {mm} { \keys_set:nn {#1 / pkg info}{#2} } 
    \NewDocumentCommand \PkgDescription {m} 
      { \noindent Package~ \textbf{\PkgInfo{#1}{name}}~Version:~\PkgInfo{#1}{version}~ -~ \PkgInfo{#1}{date}\par \emph{\PkgInfo{#1}{description}}~\par } 
  }  
%%%%%%%
%%% End of cut-n-paste
%%%%%%%


\newif\iftikzquests@@oldkeys   %  DEPRECATED, will be removed in future releases without further notice.
\tikzquests@@oldkeysfalse

\newif\iftikzquests@@nodefs
\tikzquests@@nodefsfalse

\keys_define:nn {tikzquests }
  {
    no~ alias  .usage:n = load ,
    no~ alias  .bool_set:N = \l__tikzquests_noalias_bool ,
    no~ alias  .value_forbidden:n  = true ,

    xtrakeys .usage:n = load ,
    xtrakeys .tl_set:N = \l__tikzquests_xtrakeys_tl ,
    xtrakeys .value_required:n  = true ,
    
    xtraidx .usage:n = load ,
    xtraidx .tl_set:N = \l__tikzquests_xtraidx_tl ,
    xtraidx .value_required:n  = true ,
   
    undef~ color .usage:n = load ,
    undef~ color .tl_set:N = \tikzquests@@undefcolor , % yeah... mixing styles
    undef~ color .value_required:n  = true ,
    undef~ color .initial:n = {red} ,

    in~ review .usage:n = load ,
    in~ review .bool_set:N = \l__tikzquests_inreview_bool ,

    no~ defs   .usage:n = load ,
    no~ defs   .legacy_if_gset:n = tikzquests@@nodefs ,  
    no~ defs   .value_forbidden:n  = true ,

    old keys .usage:n = load ,   %  DEPRECATED, will be removed in future releases without further notice.
    old keys .legacy_if_gset:n = tikzquests@@oldkeys ,  
    old keys .value_forbidden:n  = true ,
  }
\ProcessKeyOptions [ tikzquests ]


%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%
%%
%%
%%
%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%

\msg_new:nnnn {tikzquests} {repository already defined}
  {
    (ID:#1)~Repository~ '#2'~already~defined!
  }
  {
    You~tried~to~create~an~already~defined~Repository:~'#2'.
    ~Error~Code~ ID:<#1>.
  }

\msg_new:nnnn {tikzquests} {invalid repository}
  {
    (ID:#1)~Invalid~Repository:~ '#2'
  }
  {
    You~tried~to~use~an~invalid~Repository:~'#2'.
    ~Error~Code~ ID:<#1>.
  }

\msg_new:nnnn {tikzquests} {invalid question}
  {
    (ID:#1)~Invalid~Question:~ '#2'
  }
  {
    You~tried~to~use~an~invalid~Question:~'#2'.
    ~Error~Code~ ID:<#1>.
  }


\seq_new:N  \l__tikzquests_repositories_seq
\tl_new:N   \l__tikzquests_active_repository_tl
\seq_new:N  \l__tikzquests_tmpa_seq
\seq_new:N  \l__tikzquests_tmpb_seq
\tl_new:N   \l__tikzquests_tmp_tl  

\cs_new_protected:Npn \__tikzquests_select_repository:n #1
  {
    \prop_if_exist:cTF {l__tikzquests_ #1 _tikzrepo_prop}
    { 
      \tl_set:Nn \l__tikzquests_active_repository_tl {#1}
    }
    {  
      \msg_error:nnne {tikzquests}{invalid repository}{select~01}{#1}
    }
  }

\cs_new_protected:Npn \__tikzquests_new_repository:nn #1#2
  {
    \prop_if_exist:cTF {l__tikzquests_ #2 _tikzrepo_prop}
      {
        \msg_warning:nnne {tikzquests}{repository already defined}{new~01}{#2}
      }
      { 
        \prop_new_linked:c {l__tikzquests_ #2 _tikzrepo_prop}
        \prop_new_linked:c {l__tikzquests_ #2 _tikzrepo_remarks_prop}
        \prop_new_linked:c {l__tikzquests_ #2 _textrepo_prop}
        \prop_new_linked:c {l__tikzquests_ #2 _textrepo_remarks_prop}
        \seq_put_right:Nn \l__tikzquests_repositories_seq {#2}
      }
    \bool_if:nT {#1}
      {
        \__tikzquests_select_repository:n {#2}
      }
  }  

\__tikzquests_new_repository:nn {\c_true_bool}{default}

\NewDocumentCommand{\SelectRepository}{m}
  {
    \__tikzquests_select_repository:n {#1}
  }
  
\NewDocumentCommand{\defNewRepository}{sm}
  {
    \__tikzquests_new_repository:nn {#1}{#2}
  }  


\NewDocumentCommand{\defQuestion}{sO{\l__tikzquests_active_repository_tl}m+mO{}}
  {
    \prop_if_exist:cTF {l__tikzquests_ #2 _tikzrepo_prop}
    {
      \bool_if:nTF {#1}
        { 
          \prop_put:cnn {l__tikzquests_ #2 _textrepo_prop}{#3}{#4} 
          \prop_put:cnn {l__tikzquests_ #2 _textrepo_remarks_prop}{#3}{#5} 
        }
        { 
          \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_prop}{#3}{#4} 
          \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_remarks_prop}{#3}{#5} 
        }    
    }
    {
      \msg_error:nnne {tikzquests}{invalid repository}{def~quest~01}{#2}
    }
  }
  
\NewDocumentCommand{\defQuestionAlias}{sO{\l__tikzquests_active_repository_tl}mm}
  {
    \prop_if_exist:cTF {l__tikzquests_ #2 _tikzrepo_prop}
    {
      \bool_if:NF \l__tikzquests_noalias_bool
        {
          \bool_if:nTF {#1}
            { 
              \prop_get:cnNTF {l__tikzquests_ #2 _textrepo_prop}{#4} \l_tmpa_tl
                {
                  \prop_put:cnn {l__tikzquests_ #2 _textrepo_prop}{#3}{\prop_item:cn {l__tikzquests_ #2 _textrepo_prop}{#4}}                  
                  \prop_put:cnn {l__tikzquests_ #2 _textrepo_remarks_prop}{#3}{\prop_item:cn {l__tikzquests_ #2 _textrepo_remarks_prop}{#4}} 
                }
                {
                  \msg_error:nnnn {tikzquests}{invalid question}{alias~01}{#4}
                }
            }
            { 
              \prop_get:cnNTF {l__tikzquests_ #2 _tikzrepo_prop}{#4} \l_tmpa_tl
                {
                  \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_prop}{#3}{\prop_item:cn {l__tikzquests_ #2 _tikzrepo_prop}{#4}}                  
                  \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_remarks_prop}{#3}{\prop_item:cn {l__tikzquests_ #2 _tikzrepo_remarks_prop}{#4}} 
                }
                {
                  \msg_error:nnnn {tikzquests}{invalid question}{alias~02}{#4}
                }
            }
        }
    }
    {
      \msg_error:nnne {tikzquests}{invalid repository}{alias~03}{#2}
    }
  }
  
\cs_new_protected:Npn \__tikzquests_remarks:nnn #1#2#3
  {
    \prop_get:cnN {l__tikzquests_ #1 _remarks_prop} {#2} \l__tikzquests_tmp_tl
    \tl_if_blank:eF {\l__tikzquests_tmp_tl}
      {
        {\par\footnotesize\textbf{Remarks:~}\color{\tikzquests@@undefcolor}\l__tikzquests_tmp_tl}
      }
  }  

\cs_new_protected:Npn \__tikzquests_inreview_remarks:nnn #1#2#3
  {
    \bool_if:nT {\l__tikzquests_inreview_bool}
      {  
        \__tikzquests_remarks:nnn {#1}{#2}{#3}  
        \tl_if_blank:nF {#3}
          {
            {\par\footnotesize\textbf{Annotation:~}\color{\tikzquests@@undefcolor}#3}
          }
      }
  }  
  
  
\NewDocumentCommand{\ftikzQuestion}{D(){}O{\l__tikzquests_active_repository_tl}mO{}}
  {
			\begin{center}
        \tikzQuestion(#1)[#2]{#3}[#4]
			\end{center}
  }  
  
  
    
\NewDocumentCommand{\tikzQuestion}{D(){}O{\l__tikzquests_active_repository_tl}mO{}D<>{}}
  {
    \prop_if_exist:cTF {l__tikzquests_ #2 _tikzrepo_prop}
    {
      \prop_get:cnNTF {l__tikzquests_ #2 _tikzrepo_prop} {#3} \l__tikzquests_tmp_tl
        {
          \tl_if_blank:nTF {#1}
            {
      				\begin{tikzpicture}
                \__tikzquests_pgfkeys:n{#4}
        				\l__tikzquests_tmp_tl
      				\end{tikzpicture}
            }
            {
      				\resizebox{#1\textwidth}{!}{
      				\begin{tikzpicture}
                \__tikzquests_pgfkeys:n{#4}
        				\l__tikzquests_tmp_tl
      				\end{tikzpicture}}
            }
          \__tikzquests_inreview_remarks:nnn {#2 _tikzrepo}{#3}{#5}
        }
        {
          Invalid~Question:~#3
          \msg_error:nnnn {tikzquests}{invalid question}{quest~01}{#3}
        }
    }
    {
      Invalid~Repository:~#2
      \msg_error:nnne {tikzquests}{invalid repository}{quest~01}{#2}
    }
  }  

\cs_new_protected:Npn \__tikzquests_rawquestion:nnnnn #1#2#3#4#5
  {
    \prop_if_exist:cTF {l__tikzquests_ #1 _ #2 _prop}
    {
      \prop_get:cnNTF {l__tikzquests_ #1 _ #2 _prop} {#3} \l__tikzquests_tmp_tl
        {
          \begingroup
            \__tikzquests_pgfkeys:n{#4}
     				\l__tikzquests_tmp_tl
          \endgroup
          \__tikzquests_inreview_remarks:nnn {#1 _ #2}{#3}{#5}
        }
        {
          Invalid~Question:~#3
          \msg_error:nnnn {tikzquests}{invalid question}{quest~02}{#3}
        }
    }
    {
      Invalid~Repository:~#1
      \msg_error:nnne {tikzquests}{invalid repository}{quest~02}{#1}
    }
  }  
  
\NewDocumentCommand{\rawtikzQuestion}{O{\l__tikzquests_active_repository_tl}mO{}D<>{}}
  {
    \__tikzquests_rawquestion:nnnnn {#1}{tikzrepo}{#2}{#3}{#4}
  }

  
\NewDocumentCommand{\textQuestion}{O{\l__tikzquests_active_repository_tl}mO{}D<>{}}
  {
    \__tikzquests_rawquestion:nnnnn {#1}{textrepo}{#2}{#3}{#4}
  }

\cs_new_protected:Npn \__tikzquests_sorted_list:nnnn #1#2#3#4
  {
      \prop_if_empty:cF {l__tikzquests_ #1 _ #2 _prop}
        {
            \seq_gclear_new:N \l__tikzquests_questions_seq
            \prop_map_inline:cn {l__tikzquests_ #1 _ #2 _prop}
              {
                \seq_put_right:Nn \l__tikzquests_questions_seq {##1}
              }
            \seq_sort:Nn \l__tikzquests_questions_seq
              {
                \str_compare:eNeTF { ##1 } > { ##2 }
                  { \sort_return_swapped: }
                  { \sort_return_same: }
              }
            \begin{description}
              \seq_map_inline:Nn \l__tikzquests_questions_seq
                {
                  \item[#3{##1}]                    
                    #4[#1]{##1} \par
                    \bool_if:nF {\l__tikzquests_inreview_bool}
                      {  \__tikzquests_remarks:nnn {#1 _ #2}{##1}{}  }
                    \vspace{0.35ex}
                    \hrule\relax
                }    
            \end{description}
        }
  }


\NewDocumentCommand{\QuestionsList}{O{}}
  {
    \tl_if_blank:nTF {#1}
      { 
        \seq_set_eq:NN \l__tikzquests_tmpb_seq \l__tikzquests_repositories_seq
      }
      {
        \seq_set_from_clist:Nn \l__tikzquests_tmpb_seq {#1}
      }
      
    \seq_map_inline:Nn \l__tikzquests_tmpb_seq
      {
        \regex_match:nnTF {/$}{##1}
          {
            \tl_set:Nn \l_tmpa_tl {##1}
            \regex_replace_once:nnN {/$}{} \l_tmpa_tl
            \seq_put_right:Nn \l__tikzquests_tmpa_seq {\l_tmpa_tl}
            \seq_map_inline:Nn \l__tikzquests_repositories_seq
              {
                \regex_match:nnTF {##1}{####1}
                  {
                    \seq_put_right:Nn \l__tikzquests_tmpa_seq {####1}
                  }
                  {}
              }
          }
          {
            \seq_put_right:Nn \l__tikzquests_tmpa_seq {##1}
          }
      }      
      
    \seq_remove_duplicates:N \l__tikzquests_tmpa_seq
    \seq_sort:Nn \l__tikzquests_tmpa_seq
      {
        \str_compare:eNeTF { ##1 } > { ##2 }
          { \sort_return_swapped: }
          { \sort_return_same: }
      }
    \seq_map_inline:Nn \l__tikzquests_tmpa_seq
      {
        \prop_if_exist:cTF {l__tikzquests_ ##1 _tikzrepo_prop}
        {      
          {\vspace{1ex}Repository~ Name:~ \large\textbf{##1}}\vspace{0.35ex}\hrule\relax\hrule\relax
          {\vspace{1ex}~\hfill\emph{non~ starred~ ones~ -~ TikZ~ graphics}}\vspace{0.35ex}\hrule\relax
          \__tikzquests_sorted_list:nnnn {##1}{tikzrepo}{\raisebox{3em}}{\tikzQuestion(0.35)}
          {~\hfill\emph{starred~ ones~ -~ text/\TeX}}\vspace{0.35ex}\hrule\relax
          \__tikzquests_sorted_list:nnnn {##1}{textrepo}{}{\textQuestion}
        }
        {
          \msg_error:nnne {tikzquests}{invalid repository}{Qlist~01}{##1}
        }
      }
  }

\tl_if_empty:NTF \l__tikzquests_xtrakeys_tl
  {
    \def\tikzquests@@xtratkey{}
  }
  {
    \exp_args:NNe\def\tikzquests@@xtratkey{,\l__tikzquests_xtrakeys_tl}
  }

\tl_if_empty:NTF \l__tikzquests_xtraidx_tl
  {
    \def\tikzquests@@xtraidx{}
  }
  {
    \exp_args:NNe\def\tikzquests@@xtraidx{,\l__tikzquests_xtraidx_tl}
  }

\cs_new:Npn \__tikzquests_pgfkeys:n #1
  { \pgfkeys{\tikzquests@@ZPath, #1} }


\NewDocumentCommand{\QuestVal}{m}
  { \pgfkeys{\tikzquests@@ZPath, #1} }


\ExplSyntaxOff
%
% no easy and clean way to convert this below to expl3 (for instance underscore, under expl3 code regimè, is a letter)
%

\def\tikzquests@@ZPath{/tikzquests}


\iftikzquests@@oldkeys%  DEPRECATED, will be removed in future releases without further notice.
    \NewDocumentCommand{\tikzquests@@SetDef}{O{}m}{%
      \pgfkeys{%
    	  \tikzquests@@ZPath,%
    		keyset/.code={%
          \pgfkeysalso{%
        		  default/.append style = {#2##1={#2_{#1{##1}}}},%
        			#2/##1/.code={\csedef{#2##1}{\ensuremath{####1}}},%
              #2/##1/.value required,%
        			#2##1/.code={%
                  \ifx####1\pgfkeysnovalue%
                    \csuse{#2##1}%
                  \else%
                    \csedef{#2##1}{\ensuremath{####1}}%
                  \fi%                 
              },%
        			#2##1*/.code={\csedef{#2##1}{\ensuremath{####1}}},%
              #2##1*/.value required,%
        			#2##1 raw/.code={\csedef{#2##1}{####1}},%
              #2##1 raw/.value required,%
    		  }%
        },%
    		setaux/.code={\pgfkeysalso{keyset/.list/.expanded={##1a,##1b,##1c,##1d,##1e,##1f,##1g,##1h,##1i,##1j,##1k,##1l,##1m,##1n,##1o,##1p,##1q,##1r,##1s,##1t,##1u,##1v,##1w,##1x,##1y,##1z}}},%
    		setaux/.list/.expanded={,a,b\tikzquests@@xtraidx}%
    	}%
    }%
\else
    \iftikzquests@@nodefs
        \NewDocumentCommand{\tikzquests@@SetDef}{O{}m}{%
          \pgfkeys{%
        	  \tikzquests@@ZPath,%
        		keyset/.code={%
              \pgfkeysalso{%
            			#2##1/.initial={\ensuremath{#2_{#1{##1}}}},%
            			#2##1*/.style={#2##1=\ensuremath{####1}},%
                  #2##1*/.value required,%
            			#2##1 raw/.style={#2##1={####1}},%
                  #2##1 raw/.value required,%
              }%
            },%
        		setaux/.code={\pgfkeysalso{keyset/.list/.expanded={##1a,##1b,##1c,##1d,##1e,##1f,##1g,##1h,##1i,##1j,##1k,##1l,##1m,##1n,##1o,##1p,##1q,##1r,##1s,##1t,##1u,##1v,##1w,##1x,##1y,##1z}}},%
        		setaux/.list/.expanded={,a,b\tikzquests@@xtraidx}%
        	}%
        }%
    \else
        \NewDocumentCommand{\tikzquests@@SetDef}{O{}m}{%
          \pgfkeys{%
        	  \tikzquests@@ZPath,% 
        		keyset/.code={%
              \pgfkeysalso{%
            		  default/.append style={#2##1={#2_{#1{##1}}}},%
            			#2##1/.code={%
                      \ifx####1\pgfkeysnovalue%
                        \csuse{#2##1}%
                      \else%
                        \csedef{#2##1}{\ensuremath{####1}}%
                      \fi%                      
                  },%
            			#2##1*/.code={\csedef{#2##1}{\ensuremath{####1}}},%
                  #2##1*/.value required,%
            			#2##1 raw/.code={\csedef{#2##1}{####1}},%
                  #2##1 raw/.value required,%
              }%
            },%
        		setaux/.code={\pgfkeysalso{keyset/.list/.expanded={##1a,##1b,##1c,##1d,##1e,##1f,##1g,##1h,##1i,##1j,##1k,##1l,##1m,##1n,##1o,##1p,##1q,##1r,##1s,##1t,##1u,##1v,##1w,##1x,##1y,##1z}}},%
        		setaux/.list/.expanded={,a,b\tikzquests@@xtraidx}%
        	}%
        }%
    \fi
\fi


\AtBeginDocument{\tikzquests@@KeysDef}

\NewDocumentCommand{\tikzquests@@KeysDef}{}{%
  \pgfkeys{\tikzquests@@ZPath/.is family, \tikzquests@@ZPath, default/.style={},%
	  defset/.code={\tikzquests@@SetDef{##1}},%
		defset/.list/.expanded={R,L,C,X,Y,Z,K,T,Q,EQ\tikzquests@@xtratkey},%
		defset/.code={\tikzquests@@SetDef[f_]{##1}},%
		defset/.list/.expanded={V,I},%
		default,%
    .unknown/.code={%
        \ifx##1\pgfkeysnovalue%
          \textbf{\emph{\color{\tikzquests@@undefcolor}\pgfkeyscurrentname}}%
        \else%
          \pgfkeysalso{\pgfkeyscurrentname/.initial={##1}}
        \fi%
    }
	}%
}%
