%% rbt-mathnotes-util.sty
%% Copyright 2021 Rebecca B. Turner.
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% 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.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is Rebecca B. Turner.
%
% This work consists of the files:
%     README.md
%     rbt-mathnotes.tex
%     rbt-mathnotes.sty
%     rbt-mathnotes.cls
%     rbt-mathnotes-util.sty
%     rbt-mathnotes-messages.sty
%     rbt-mathnotes-hw.cls
%     rbt-mathnotes-formula-sheet.cls
%     examples/cheat-sheet.tex
%     examples/multivar.tex
%     examples/topology-hw-1.tex
% and the derived files:
%     rbt-mathnotes.pdf
%     examples/cheat-sheet.pdf
%     examples/multivar.pdf
%     examples/topology-hw-1.pdf

\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\ProvidesExplPackage{rbt-mathnotes-util}{2021/11/29}{1.0.2}{Utility commands
  for rbt-mathnotes.}

\cs_set:Npn \mn__legacy_bool_option:n #1
  {
    \bool_new:c { g__mn_#1_bool }
    \csname if\@currname @#1 \endcsname
      \bool_gset_true:c { g__mn_#1_bool }
    \else
      \bool_gset_false:c { g__mn_#1_bool }
    \fi
  }

\keys_define:nn { mn_options }
  {
    init .tl_set:N = \l__mn_options_init_tl ,
    init .default:n = true ,
    init .initial:x = \tl_use:N \c_novalue_tl ,

    default .tl_set:N = \l__mn_options_default_tl ,
    default .value_required:n = true ,
    default .initial:x = \tl_use:N \c_novalue_tl ,

    type .choice: ,
    type .choices:nn = { bool, str }
      { \tl_set_eq:NN \l__mn_options_type_tl \l_keys_choice_tl } ,
    type .value_required:n = true ,
    type .initial:n = bool ,
  }

% A hook which executes after \ProcessKeyvalOptions and is used to convert
% TeX booleans and so on to LaTeX3 booleans and strings.
\tl_new:N \g__mn_after_options_hook_tl
% A hook used to define a particular option, allowing keys to be set only in
% a group.
\tl_new:N \g__mn_define_option_tl
% Initializes complementary #1 and no#1 package options.
\NewDocumentCommand \mn__option_new { o m }
  {
    \tl_gclear:N \g__mn_define_option_tl
    \group_begin:
    \tl_if_novalue:nF { #1 }
      { \keys_set:nn { mn_options } { #1 } }

    \tl_set:Nn \l_tmpa_tl { bool }
    \tl_if_eq:NNT \l__mn_options_type_tl \l_tmpa_tl
      {
        % If no default was given, use `false` for bools.
        \tl_if_eq:NNT \l__mn_options_init_tl \c_novalue_tl
          { \tl_set:Nn \l__mn_options_init_tl { false } }

        \tl_gput_right:Nx \g__mn_define_option_tl
          {
            \exp_not:N \DeclareBoolOption [\l__mn_options_init_tl]{#2}
            \exp_not:N \DeclareComplementaryOption {no#2}{#2}
          }

        % After we process options, convert to an expl3 bool.
        \tl_gput_right:Nn \g__mn_after_options_hook_tl
          {
            % Adapt the legacy bool into an expl3 bool.
            \mn__legacy_bool_option:n { #2 }
            % Then, delete the legacy bool.
            \mn__legacy_bool_undefine:n { \@currname @#2 }
          }
      }

    % For a string option:
    \tl_set:Nn \l_tmpa_tl { str }
    \tl_if_eq:NNT \l__mn_options_type_tl \l_tmpa_tl
      {
        \tl_gput_right:No \g__mn_define_option_tl
          {
            \exp_not:N \DeclareStringOption
              \tl_if_eq:NNF \l__mn_options_init_tl \c_novalue_tl
                { [\l__mn_options_init_tl] }
              { #2 }
              \tl_if_eq:NNF \l__mn_options_default_tl \c_novalue_tl
                { [\l__mn_options_default_tl] }
          }

          \tl_gput_right:Nn \g__mn_after_options_hook_tl
            {
              % Create the new tl for this option.
              \tl_new:c { g__mn_#2_tl }
              % Set it to the command kvoptions created.
              \tl_set:co { g__mn_#2_tl } { \cs:w \@currname @#2 \cs_end: }
              % Delete the old command.
              \cs_undefine:c { \@currname @#2 }
            }
      }
    \group_end:
    \tl_use:N \g__mn_define_option_tl
  }

\cs_set:Npn \mn__process_options:n #1
  {
    \ProcessKeyvalOptions { #1 }
    \tl_use:N \g__mn_after_options_hook_tl
  }

\cs_set:Npn \mn__legacy_bool_undefine:n #1
  {
    \cs_undefine:c { if#1 }
    \cs_undefine:c { #1true }
    \cs_undefine:c { #1false }
  }

% Package configuration string values.
% {< module >}{< family >}{< key name >}
\cs_set:Npn \mn__key_new:nnn #1#2#3
  {
    \tl_set_eq:cN { l__#1_#3_tl } \c_novalue_tl
    \keys_define:nn { #2 }
      {
        #1 .value_required:n = true,
        #1 .tl_set:c = l__#1_#3_tl,
      }
  }

\cs_set:Npn \mn__mathnotes_key_new:n #1
  {
    \mn__key_new:nnn { mn } { mathnotes } { #1 }
  }

\cs_set:Npn \mn__keys_new:n #1 { \clist_map_function:nN { #1 } \mn__mathnotes_key_new:n }

\prg_new_conditional:Npnn \mn__if_package_loaded:n #1
  { p, T, F, TF }
  {
    \@ifpackageloaded { #1 }
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \mn__if_novalue:N #1
  { p, T, F, TF }
  {
    \tl_if_eq:NNTF \c_novalue_tl #1
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \mn__if_novalue:N
  { c }
  { p, T, F, TF }