__HEADER__(Grouping theorems for easier customization.)


\ExplSyntaxOn

\RequirePackage{amsthm}
\RequirePackage{thmtools}
\RequirePackage{mkessler-powerset}

\hook_new:n { @@/prename }
\hook_new:n { @@/postname }
\hook_new:n { @@/mapname }
\hook_new:n { @@/thmtools }

\hook_new:n { @@/groupsort }

%%TODO move this to fancythm
\newcounter{indocument}
\newcounter{insection}[section]
\newcounter{insubsection}[subsection]

\renewcommand\theinsection   { \thesection.\arabic{insection}       }
\newcommand\theinsubection { \thessubection.\arabic{insubsection} }


%%% LaTeX3 Wrappers around external commands

\cs_new:Nn \@@_declare_theorem:nn
{
  \declaretheorem [ #2 ] { #1 }
}

\cs_generate_variant:Nn \@@_declare_theorem:nn { n V }
\cs_generate_variant:Nn \hook_gset_rule:nnnn { n n V n }


%%% Messages

\msg_new:nnn { groupthm } { unknown ~ key }
{
  Unknown ~  key ~ '#1' ~ supplied ~ \msg_line_context:
}

\msg_new:nnn { groupthm } { unknown ~ group }
{
  Unknown  ~ group ~ '#1' ~ supplied ~ \msg_line_context:
}

%%% Variables

\tl_new:N    \l_@@_key_prename_tl
\tl_new:N    \l_@@_key_name_tl
\tl_new:N    \l_@@_key_postname_tl
\clist_new:N \l_@@_key_group_clist
\clist_new:N \l_@@_key_mapname_clist
\clist_new:N \l_@@_key_thmtools_clist

\tl_new:N    \l_@@_prename_tl
\tl_new:N    \l_@@_name_tl
\tl_new:N    \l_@@_postname_tl
\tl_new:N    \l_@@_mapname_clist
\tl_new:N    \l_@@_thmtools_clist

\tl_new:N    \l_@@_relation_tl

\clist_new:N \l_@@_group_clist

\clist_new:N \g_groupthm_defined_theorem_groups_clist

%%% Keys

\keys_define:nn { groupthm }
  {
    prename      .tl_set:N      = \l_@@_key_prename_tl,
    prename      .default:n     = \c_empty_tl,
    prename      .groups:n      = { theoremgroup },
    name         .tl_set:N      = \l_@@_key_name_tl,
    name         .default:n     = \c_novalue_tl,
    name         .groups:n      = { groupedtheorem, theoremvariants },
    postname     .tl_set:N      = \l_@@_key_postname_tl,
    postname     .default:n     = \c_empty_tl,
    postname     .groups:n      = { theoremgroup },
    group        .clist_set:N   = \l_@@_key_group_clist,
    group        .default:n     = {},
    group        .groups:n      = { groupedtheorem, theoremvariants },
    mapname      .clist_set:N   = \l_@@_mapname_clist,
    mapname      .default:n     = {},
    mapname      .groups:n      = { theoremgroup },
    thmtools     .clist_set:N   = \l_@@_key_thmtools_clist,
    thmtools     .default:n     = {},
    thmtools     .groups:n      = { theoremgroup, groupedtheorem, theoremvariants },
    unknown      .code:n        = \msg_error:nnn { groupthm } { unknown ~ group } { \l_keys_key_str }
  }


% groupname, prename, postname, mapname, thmtools
\cs_new:Npn \new_theorem_group:nnnnn #1#2#3#4#5
{
  \cs_new:cpn { group_use_#1: }
    {
      \hook_gput_code:nnn { @@/prename } { #1 }
        {
            \tl_put_left:Nx \l_@@_prename_tl { #2 }
        }
      \hook_gput_code:nnn { @@/postname } { #1 }
        {
            \tl_put_right:Nx \l_@@_postname_tl { #3 }
        }
      \hook_gput_code:nnn { @@/mapname } { #1 }
        {
          \clist_put_right:Nn \l_@@_mapname_clist { #4 }
        }
      \hook_gput_code:nnn { @@/thmtools } { #1 }
        {
          \clist_put_right:Nn \l_@@_thmtools_clist { #5 }
        }
    }



    \clist_gput_left:Nn \g_groupthm_defined_theorem_groups_clist { #1 }
}
\cs_generate_variant:Nn \new_theorem_group:nnnnn { n V V V V }



\cs_new:Npn \@@_undeclare_theorem_group_aux:n #1
{
  \cs_undefine:c { @@_use_group_#1:   }
  \hook_gremove_code:nn { @@/prename  }
  \hook_gremove_code:nn { @@/postname }
  \hook_gremove_code:nn { @@/mapname  }
  \hook_gremove_code:nn { @@/thmtools }
  \clist_remove_all:Nn  \g_@@_defined_theorem_groups_clist { #1 }
  
  \@@_hook_gset_rule_foreach:nNnn
    { ?? }
    \g_@@_defined_theorem_groups_clist { #1 }
    { unrelated }
    { #1 }

  \@@_hook_gset_rule_foreach:nNnn
    { @@/prename }
    \g_@@_defined_theorem_groups_clist { #1 }
    { unrelated }
    { #1 }

  \@@_hook_gset_rule_foreach:nNnn
    { @@/postname }
    \g_@@_defined_theorem_groups_clist { #1 }
    { unrelated }
    { #1 }

  \@@_hook_gset_rule_foreach:nNnn
    { @@/mapname }
    \g_@@_defined_theorem_groups_clist { #1 }
    { unrelated }
    { #1 }

  \@@_hook_gset_rule_foreach:nNnn
    { @@/thmtools }
    \g_@@_defined_theorem_groups_clist { #1 }
    { unrelated }
    { #1 }
}


% hook, list of labels, relation, label
\cs_new:Npn \@@_hook_gset_rule_foreach:nNnn #1 #2 #3 #4
  {
    \cs_set:Npn \@@_map_aux:n
    {
      \hooks_gset_rule:nnnn { #1 } { ##1 } { #3 } { #4 }
    }
    \clist_map_function:NN #2 \@@_map_aux:n
  }


\cs_new:Npn \@@_update_ordering:n #1
{
  \@@_hook_gset_rule_foreach:nNnn
    { @@/groupsort }
    \g_@@_defined_theorem_groups_clist
    { before }
    { #1 }
}

\cs_new:Npn \new_theorem_group_by_keys:nn #1#2
{
  \@@_set_normalized_keys:nnn { #1 } { theoremgroup } { #2 }
  \new_theorem_group:nVVVV { #1 }
    \l_@@_prename_tl
    \l_@@_postname_tl
    \l_@@_mapname_clist
    \l_@@_thmtools_clist
}

\NewDocumentCommand{\NewTheoremGroup}{ O{} m }
{
  \new_theorem_group_by_keys:nn { #2 } { #1 }
}


\cs_new:Npn \groupthm_new_theorem_group:nnnnn #1 #2 #3 #3 #5
  {
    \cs_if_exist:cTF { @@_use_group_#1 }
      {
        \msg_error:nnnn { groupthm } { wrong ~ definition }
          { group } { #1 } { already }
      }
      {
        \@@_new_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 }
      }
  }

\cs_new:Npn \groupthm_renew_theorem_group:nnnnn #1 #2 #3 #3 #5
  {
    \cs_if_exist:cTF { @@_use_group_#1 }
      {
        \@@_undeclare_theorem_aux:nnnnn { #1 }
        \@@_declare_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 }
      }
      {
        \msg_error:nnnn { groupthm } { wrong ~ definition }
          { group } { #1 } { not }
      }
  }

\cs_new:Npn \groupthm_provide_theorem_group:nnnnn #1 #2 #3 #3 #5
  {
    \cs_if_exist:cF { @@_use_group_#1 }
      {
        \@@_new_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 }
      }
  }

\cs_new:Npn \groupthm_declare_theorem_group:nnnnn #1 #2 #3 #3 #5
  {
    \cs_if_exist:cT { @@_use_group_#1 }
      {
        \@@_undeclare_theorem_aux:nnnnn { #1 }
      }
      \@@_declare_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 }
  }


%%% Documented until here


%%% Grouped Theorems

\cs_new:Npn \group_use:n #1
{
  \cs_if_exist_use:cF { group_use_#1: }
  {
    \msg_error:nnn { groupthm } { unknown ~ group } { #1 }
  }
}

\cs_new:Npn \map_use_on_name:n #1
{
  \tl_set:Nx \l_@@_name_tl
  {
    #1 { \tl_use:N \l_@@_name_tl }
  }
}

% envname, groups, name, thmtools
\cs_new:Npn \new_grouped_theorem:nnnn #1 #2 #3 #4
{
  

  \tl_clear:N     \l_@@_prename_tl
  \tl_set:Nn      \l_@@_name_tl        { #3 }
  \tl_clear:N     \l_@@_postname_tl
  \clist_clear:N  \l_@@_mapname_clist
  \clist_set:Nn   \l_@@_thmtools_clist { #4 }

  \hook_gremove_code:nn { @@/prename  }{*}
  \hook_gremove_code:nn { @@/postname }{*}
  \hook_gremove_code:nn { @@/mapname  }{*}
  \hook_gremove_code:nn { @@/thmtools }{*}
  
  \clist_map_function:nN { #2 } \group_use:n

%  \ShowHook{ @@/postname }
  \hook_use:n { @@/prename }
  \hook_use:n { @@/postname }
  \hook_use:n { @@/mapname }
  \hook_use:n { @@/thmtools }

  \clist_map_function:NN \l_@@_mapname_clist \map_use_on_name:n
  
  \clist_put_right:Nx \l_@@_thmtools_clist
  {
    name = \tl_use:N \l_@@_prename_tl
           \tl_use:N \l_@@_name_tl
           \tl_use:N \l_@@_postname_tl
  }

  \@@_declare_theorem:nV
    { #1 }
    \l_@@_thmtools_clist
}
\cs_generate_variant:Nn \new_grouped_theorem:nnnn { n V V V }
\cs_generate_variant:Nn \new_grouped_theorem:nnnn { x V n n }

\cs_new:Npn \groupthm_provide_grouped_theorem:nnnn
  {
    \cs_if_exist:cF { #1 }
      {
        \@@_groupthm_declare_grouped_theorem_aux:nnnn
          { #1 } { #2 } { #3 } { #4 }
      }
  }


\cs_new:Npn \groupthm_new_theorem:nnnn #1 #2 #3 #4
  {
    \groupthm_new_grouped_theorem:nnnn
      { #1 } { #2 } { #3 } { #4 }
    \groupthm_new_grouped_theorem:nnnn
      { #1* } { #2, starred } { #3 } { #4 }
  }


% keys, subgroup
\cs_new:Npn \@@_set_normalized_keys:nnn #1 #2 #3
  {
    \keys_set:nn { @@ } { prename, name, postname, group, mapname, thmtools }
    \keys_set_groups:nnn { @@ } { #2 } { #1 }

    \tl_if_eq:NnTF \l_@@_key_name_tl { \c_novalue_tl }
      {
        \tl_set:Nx \l_@@_name_tl
          {
            \text_titlecase_first:n {#3}
          }
      }
      {
        \tl_set_eq:NN \l_@@_name_tl \l_@@_key_name_tl
      }

    \tl_set_eq:NN    \l_@@_prename_tl     \l_@@_key_prename_tl
    \tl_set_eq:NN    \l_@@_postname_tl    \l_@@_key_postname_tl
    \clist_set_eq:NN \l_@@_group_clist    \l_@@_key_group_clist
    \clist_set_eq:NN \l_@@_mapname_clist  \l_@@_key_mapname_clist
    \clist_set_eq:NN \l_@@_thmtools_clist \l_@@_key_group_clist
  }


\cs_new:Npn \groupthm_provide_theorem_star:nnnn #1 #2 #3 #4
{
  \groupthm_provide_theorem:nnnn
    { #1 } { #2, unnumbered } { #3 } { #4 }
}

% envname, keys
\cs_new:Npn \new_grouped_theorem_from_keys:nn #1 #2
{
  \@@_set_normalized_keys { #2 } { groupedtheorem } { #1 }

  \new_grouped_theorem:nVVV
    { #1 }
    \l_@@_key_group_clist
    \l_@@_name_tl
    \l_@@_key_thmtools_clist
}

\NewDocumentCommand{\NewGroupedTheorem}{O{} m}
{
  \new_grouped_theorem_from_keys:nn { #2 } { #1 }
}


\groupthm_new_grouped_theorem_from_keys:nn
{

}


%% Rules for different theorem groups

\cs_generate_variant:Nn \hook_gset_rule:nnnn { n n V n }

% hook group1 relation group2
\cs_new:Npn \declare_theorem_group_rule:nnnn #1 #2 #3 #4
{
  \str_set:Nx \l_tmpa_str { \tl_trim_spaces:n { #3 } }

  \str_if_eq:VnT \l_tmpa_str { higher }
  {
    \str_set:Nn \l_tmpa_tl { after }
  }

  \str_if_eq:VnT \l_tmpa_str{ lower }
  {
    \str_set:Nn \l_tmpa_tl { before }
  }

  \str_if_eq:nnTF { #1 } { ?? }
  {
    \hook_gset_rule:nnVn {??} {#2} \l_tmpa_tl {#4}
  }
  {
    \hook_gset_rule:nnVn { @@ / #1 } {#2} \l_tmpa_tl {#4}
  }
}

% hook, group1, relation, group2
\NewDocumentCommand { \DeclareTheoremGroupRule } { O{??} m m m }
{
  \declare_theorem_group_rule:nnnn {#1} {#2} {#3} {#4}
}


%%% Hacks for sorting groupnames

\cs_new:Npn \__add_to_sort_hook:n #1
{
  \hook_gput_code:nnn { @@/groupsort }
  { #1 }
  {
    \clist_put_left:Nn \l_@@_group_clist { #1 }
  }
}

\cs_new:Npn \__sort_group_names:
{
  \hook_gremove_code:nn { @@/groupsort  }{*}
  \clist_map_function:NN \l_@@_group_clist \__add_to_sort_hook:n
  \clist_clear:N \l_@@_group_clist
  \hook_use:n { @@/groupsort }
}


%%% Theorem variants generation

\ExplSyntaxOn

% envname, groups, name, thmtools list, extra groups (not in powerset)
\cs_new:Npn \@@_new_grouped_theorem_family_aux:nnnnnn
  {
    \clist_set:Nn \l_tmpa_clist { #2 }
    \powerset_clist_foreach:Nn \l_tmpa_clist
      {
        \clist_set_eq:NN \l_@@_group_clist \l_tmpa_clist
        \clist_put_right:Nn \l_@@_group_clist { #5 }
        \__sort_group_names:

        \use:c{groupthm_#6_grouped_theorem:xVnn}
          {__#1__groups_\clist_use:Nn \l_@@_group_clist {_}}
          \l_@@_group_clist
          { #3 }
          { #4 }
      }
  }


% envname, name, thmtools, list of groups
\cs_new:Npn \generate_theorem_variants:nnnn #1 #2 #3 #4
{
  \clist_set:Nn \l_@@_group_clist { #4 }
  \powerset_clist_foreach:Nn \l_@@_group_clist
  {
    \__sort_group_names:

    \new_grouped_theorem:xVnn
      {__#1__groups_\clist_use:Nn \l_@@_group_clist {_}}
      \l_@@_group_clist
      { #2 }
      { #3 }
  }
}
\cs_generate_variant:Nn \generate_theorem_variants:nnnn { n V V V }


\cs_new:Npn \groupthm_new_grouped_theorem_family_from_keys:nn #1 #2
{
  \@@_set_normalized_keys:nn { #1 } { #2 }
  \groupthm_new_grouped_theorem_family:nVVV
    { #2 }
    \l_@@_groups_clist
    \l_@@_name_tl
    \l_@@_thmtools_clist
}



% envname, keys
\cs_new:Npn \generate_theorem_variants_from_keys:nn #1 #2 
{
  \keys_set_groups:nnn { groupthm } { theoremvariants } { name, thmtools, group }
  \keys_set_groups:nnn { groupthm } { theoremvariants } { #2 }

  \tl_if_eq:NnTF \l_@@_key_name_tl { \c_novalue_tl }
    {
      \tl_set:Nx \l_@@_name_tl
        {
          \text_titlecase_first:n {#1}
        }
    }
    {
      \tl_set_eq:NN \l_@@_name_tl \l_@@_key_name_tl
    }

  \clist_set_eq:NN \l_@@_group_clist \l_@@_key_group_clist
  \clist_put_left:Nn \l_@@_group_clist { starred }

  \generate_theorem_variants:nVVV
    { #1 }
    \l_@@_name_tl
    \l_@@_key_thmtools_clist
    \l_@@_group_clist
}

\NewDocumentCommand { \GenerateTheoremVariants } { O{} m }
{
  \generate_theorem_variants_from_keys:nn { #2 } { #1 }
}

%%% Theorem variants declaration / parsing
\cs_new:Npn \add_theorem_to_group:n #1
{
  \bool_if:NTF \l_@@_in_family_options_environment_bool
  {
    \clist_put_left:Nn \l_@@_group_clist { #1 }
  }
  {
    \msg_error:nn { groupthm } { misuse ~ add ~ theorem ~ to ~ group }
  }
}

% envname, signature, definition, always groups, backend
\cs_new:Npn \__new_theorem_variant_parser_aux:nnnn #1 #2 #3 #4 #5
{
  \use:c{ #5 DocumentEnvironment }
    { #1 }
    { #2 }
    {
      \clist_clear:N \l_@@_group_clist

      #3
      
      \clist_put_right:NV \l_@@_group_clist { #4 }
      \__sort_group_names:
      \begin { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } }
    }
    {
      \clist_clear:N \l_@@_group_clist

      #3

      \clist_put_right:NV \l_@@_group_clist { #4 }
      \__sort_group_names:
      \end { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } }
    }
}

% envname, signature, definition
\cs_new:Npn \new_theorem_variant_parser:nnn #1 #2 #3
{
  \__new_theorem_variant_parser_aux:nnnn { #1 } { #2 } { #3 } { \BooleanTrue }
  \__new_theorem_variant_parser_aux:nnnn { #1 } { #2 } { #3 } { \BooleanFalse }
}

%% Exposing clean interface for parsing theorem variants

\NewDocumentCommand { \AddTheoremToGroup } { m }
{
  \add_theorem_to_group:n { #1 }
}

\NewDocumentCommand{ \DeclareTheoremVariants }{ m m m }
{
  \new_theorem_variant_parser:nnn { #1 } { #2 } { #3 }
}


%%%%% Convenience macros for usage of this package

% evname, star?
\cs_new:Npn \declare_standard_theorem_variants:n #1
{
  \DeclareTheoremVariants { #1 } { !s !t+ }
  {
    \IfBooleanT{##1}
    {
      \AddTheoremToGroup { star }
    }
    \IfBooleanT{##2}
    {
      \AddTheoremToGroup { dagger }
    }
  }
}

% envname, name, thmtools
\cs_new:Npn \generate_standard_theorem_variants:nnn #1 #2 #3 
{
  \generate_theorem_variants:nnnn { #1 } { #2 } { #3 } { star, dagger, starred }
}


% envname, keys
\cs_new:Npn \generate_standard_theorem_variants_from_keys:nn
{
  % TODO
}

\NewDocumentCommand{\GenerateDefaultTheoremVariants}{ O{} m }
{
  \generate_standard_theorem_variants_from_keys:nn { #2 } { #1 }
}

%%% Default groups available

\NewTheoremGroup
[
]{ all }

\NewTheoremGroup
[
  thmtools = { numbered = no }
] { starred }

\NewTheoremGroup
[
  thmtools = { numbered = no }
] { unnumbered }

%%

\NewTheoremGroup
[
  thmtools = { sibling = insection }
] { big }

\NewTheoremGroup
[
  postname = { $^{\dagger}$ },
  thmtools = { sibling = insubsection }
] { dagger }

\NewTheoremGroup
[
  thmtools = { sibling = insubsection }
] { small }

\NewTheoremGroup
[
  thmtools = { sibling = insubsection },
  postname = { * },
] { star }


\DeclareTheoremGroupRule { big     } { incompatible-error } { small  }

\DeclareTheoremGroupRule { all     } { lower              } { big    }
\DeclareTheoremGroupRule { all     } { lower              } { small  }
\DeclareTheoremGroupRule { all     } { lower              } { dagger }
\DeclareTheoremGroupRule { all     } { lower              } { star   }

\DeclareTheoremGroupRule { starred } { higher             } { big    }
\DeclareTheoremGroupRule { starred } { higher             } { small  }
\DeclareTheoremGroupRule { starred } { higher             } { dagger }
\DeclareTheoremGroupRule { starred } { higher             } { star   }