% ----------------------------------------------------------------------------
% the XSIM package - solutions module
% 
%   eXercise Sheets IMproved
% 
% ----------------------------------------------------------------------------
% Clemens Niederberger
% Web:    https://github.com/cgnieder/xsim
% E-Mail: contact@mychemistry.eu
% ----------------------------------------------------------------------------
% Copyright 2017--2020 Clemens Niederberger
% 
% 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 Clemens Niederberger.
% ----------------------------------------------------------------------------
% If you have any ideas, questions, suggestions or bugs to report, please
% feel free to contact me.
% ----------------------------------------------------------------------------
\XSIMmodule{solutions}{managing solutions}

\xsim_load_modules:n {tags,properties}

\bool_new:N \l_xsim_inside_solution_bool
\bool_new:N \l__xsim_include_question_bool
\bool_new:N \l__xsim_printsolutions_headings_bool
\bool_new:N \l__xsim_printsolutions_conditions_bool
\bool_new:N \l____xsim_this_condition_bool

\prop_new:N \l__xsim_printsolutions_conditions_prop

\int_new:N  \l_xsim_printsolutions_section_int
\int_new:N  \l_xsim_printsolutions_chapter_int

\tl_new:N   \l__xsim_printsolutions_headings_template_tl
\tl_new:N   \ExerciseSection
\tl_new:N   \ExerciseChapter
\tl_new:N   \l__xsim_printsolutions_collection_tl

% ----------------------------------------------------------------------------

\prg_new_conditional:Npnn \xsim_if_inside_solution: {p,T,F,TF}
  {
    \bool_if:NTF \l_xsim_inside_solution_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_solution_print:n #1 {p,T,F,TF}
  {
    \bool_if:cTF {l__xsim_#1_solution_print_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: type
% #2: id
\prg_new_protected_conditional:Npnn \xsim_solution_if_print:nn #1#2 {T,F,TF}
  {
    \bool_set_true:N \l____xsim_print_bool
    \xsim_if_tagged:nnF {#1} {#2}
      { \bool_set_false:N \l____xsim_print_bool }
    \xsim_if_boolean_property:nnnF {#1} {#2} {print}
      { \bool_set_false:N \l____xsim_print_bool }
    \bool_if:cF {l__xsim_#1_solution_print_bool}
      { \bool_set_false:N \l____xsim_print_bool }
    \bool_if:NTF \l____xsim_print_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_solution_if_print:nnT {nV,oo}

% #1: type
% #2: id
\prg_new_protected_conditional:Npnn \xsim_if_solution_exist:nn #1#2 {T,F,TF}
  {
    \xsim_if_boolean_property:nnnTF {#1} {#2} {solution}
      { \xsim_if_exist:nnnTF {#1} {#2} {exercise} }
      { \xsim_if_exist:nnnTF {#1} {#2} {solution} }
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_solution_exist:nnT  {VV,oo}
\cs_generate_variant:Nn \xsim_if_solution_exist:nnTF {oo}

% #1: type
% #2: options
\cs_new_protected:Npn \xsim_start_solution:nn #1#2
  {
    \keys_set:xn { xsim/\xsim_get_parameter:nn {#1} {solution-env} } {#2}
    \bool_set_true:N \l_xsim_inside_solution_bool
    \refstepcounter { \xsim_get_parameter:nn {#1} {solution-counter} }
  }

\cs_new_protected:Npn \xsim_stop_solution:n #1 {}

% ----------------------------------------------------------------------------

% #1: conditional base function (three n args)
% #2: name
% #3: code (refer to type and id with `#1' and `#2'), #3 refers to the boolean
%     properties use/print
\cs_new_protected:Npn \xsim_new_solutions_condition:Nnn #1#2#3
  {
    \prg_new_protected_conditional:Npnn #1 ##1##2##3 {T,F,TF}
      {
        \xsim_if_in_list:nnTF {##3} {##1-##2=={true}}
          {
            \__xsim_printsolutions_if_condition:nTF {#2}
              { \__xsim_printsolutions_condition_true: #3 }
              { \prg_return_false: }
          }
          { \prg_return_false: }
      }
  }

% per section condition:
\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_per_section:nnn
  {per-section}
  {
    \xsim_if_in_list:nxTF {section-value}
      { #1-#2 == { \int_use:N \l_xsim_printsolutions_section_int } }
      {
        \tl_set:Nx \ExerciseSection
          { \xsim_get_property:nnn {#1} {#2} {section} }
        \prg_return_true:   
      }
      { \prg_return_false: }
  }

% per chapter condition:
\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_per_chapter:nnn
  {per-chapter}
  {
    \xsim_if_in_list:nxTF {chapter-value}
      { #1-#2 == { \int_use:N \l_xsim_printsolutions_chapter_int } }
      {
        \tl_set:Nx \ExerciseChapter
          { \xsim_get_property:nnn {#1} {#2} {chapter} }
        \prg_return_true:   
      }
      { \prg_return_false: }
  }

% per collection condition:
\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_in_collection:nnn
  {per-collection}
  {
    \tl_if_blank:VTF \l__xsim_printsolutions_collection_tl
      { \prg_return_false: }
      {
        \xsim_if_in_collection:VnnTF
          \l__xsim_printsolutions_collection_tl
          {#1}
          {#2}
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

% ----------------------------------------------------------------------------
% conditions mechanism
\cs_new_protected:Npn \__xsim_printsolutions_condition_false:
  { \bool_set_false:N \l__xsim_printsolutions_conditions_bool }

\cs_new_protected:Npn \__xsim_printsolutions_condition_true:
  { \bool_set_true:N \l__xsim_printsolutions_conditions_bool }

% #1: type
% #2: id
% #3: boolean property
\prg_new_conditional:Npnn
  \__xsim_printsolutions_if_conditions:nnn #1#2#3 {T,F,TF}
  {
    \bool_if:NTF \l__xsim_printsolutions_conditions_bool
      { \prg_return_true: }
      {
        \xsim_if_in_list:nnTF {#3} {#1-#2=={true}}
          { \prg_return_false: }
          { \prg_return_true: }
      }
  }

% #1: name
% #2: condition
\cs_new_protected:Npn \xsim_declare_printsolutions_condition:nn #1#2
  {
    \bool_new:c {l__xsim_printsolutions_#1_condition_bool}
    \prop_put:Nnn \l__xsim_printsolutions_conditions_prop {#1} {#2}
  }

\prg_new_conditional:Npnn \__xsim_printsolutions_if_condition:n #1 {T,F,TF}
  {
    \bool_if:cTF {l__xsim_printsolutions_#1_condition_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: name
% #2: true|false
\cs_new_protected:Npn \__xsim_printsolutions_condition:nn #1#2
  { \bool_set_eq:cc {l__xsim_printsolutions_#1_condition_bool} {c_#2_bool} }

% false if any condition is false for this solution:
% #1: type
% #2: id
% #3: boolean property
\prg_new_protected_conditional:Npnn
  \__xsim_printsolutions_check_conditions:nnn #1#2#3 {T,F,TF}
  {
    \__xsim_printsolutions_condition_false:
    \bool_set_true:N \l____xsim_this_condition_bool
    \prop_map_inline:Nn \l__xsim_printsolutions_conditions_prop
      {
        \bool_if:cT {l__xsim_printsolutions_##1_condition_bool}
          {
            ##2 {#1} {#2} {#3}
              { }
              { \bool_set_false:N \l____xsim_this_condition_bool }
          }
      }
    \bool_if:NTF \l____xsim_this_condition_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }


% #1: type
% #2: id
% #3: boolean property
% #4: code
\cs_new_protected:Npn \xsim_if_this_solution_do:nnnn #1#2#3#4
  {
    \xsim_if_solution_exist:nnT {#1} {#2}
      { \__xsim_printsolutions_check_conditions:nnnT {#1} {#2} {#3} {#4} }
  }

% #1: type
% #2: boolean property
% #3: code
\cs_new_protected:Npn \xsim_if_solutions_do:nnn #1#2#3
  {
    \xsim_foreach_exercise_type_id:nn {#2}
      { \xsim_if_this_solution_do:nnnn {#1} {##2} {#2} {#3} }
  }

% ----------------------------------------------------------------------------
% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: pre
% #3: post
% #4: type
% #5: code (refer to type with #1 and id with #2)
\cs_new_protected:Npn \xsim_print_type_code:nnnnn #1#2#3#4#5
  {
    \group_begin:
      \cs_set_protected:Npn \__xsim_print_entry:nn ##1##2 {#5}
      #2
      \bool_set_false:N \l__xsim_tmpa_bool
      \bool_if:nTF {#1}
        {
          \xsim_if_solutions_do:nnn {#4} {print}
            { \bool_set_true:N \l__xsim_tmpa_bool }
        }
        {
          \xsim_if_solutions_do:nnn {#4} {used}
            { \bool_set_true:N \l__xsim_tmpa_bool }
        }
      \tl_set:Nn \ExerciseType {#4}
      \bool_if:NT \l__xsim_tmpa_bool
        {
          \bool_if:NT \l__xsim_printsolutions_headings_bool
            {
              \xsim_use_template:nV
                {heading}
                \l__xsim_printsolutions_headings_template_tl
            }
        }
      \bool_if:nTF {#1}
        {
          \xsim_if_solutions_do:nnn {#4} {print}
            { \__xsim_print_entry:nn {#4} {##2} }
        }
        {
          \xsim_if_solutions_do:nnn {#4} {used}
            { \__xsim_print_entry:nn {#4} {##2} }
        }
      #3
    \group_end:
  }

% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: options
% #3: type
\cs_new_protected:Npn \xsim_print_type_solutions:nnn #1#2#3
  {
    \xsim_print_type_code:nnnnn
      {#1}
      {
        \xsim_verbose:n
          { Printing~ solutions~ for~ exercise~ type~ `#3'. }
        \keys_set:nn {xsim/print-solutions} {#2}
      }
      {}
      {#3}
      { \xsim_insert:nnnn {##1} {##2} {} {solution} }
  }

% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: options
\cs_new_protected:Npn \xsim_print_all_solutions_per_type:nn #1#2
  {
    \xsim_foreach_exercise_type:n
      { \xsim_print_type_solutions:nnn {#1} {#2} {##1} }
  }

% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: options
\cs_new_protected:Npn \xsim_print_all_solutions_per_id:nn #1#2
  {
    \xsim_verbose:n { Printing~ solutions. }
    \group_begin:
      \keys_set:nn {xsim/print-solutions} {#2}
      \bool_set_false:N \l__xsim_tmpa_bool
      \bool_if:nTF {#1}
        {
          \xsim_foreach_exercise_id_type:nn {print}
            {
              \xsim_if_this_solution_do:nnnn {##1} {##2} {print}
                { \xsim_insert:nnnn {##1} {##2} {} {solution} }
            }
        }
        {
          \xsim_foreach_exercise_id_type:nn {used}
            {
              \xsim_if_this_solution_do:nnnn {##1} {##2} {used}
                { \xsim_insert:nnnn {##1} {##2} {} {solution} }
            }
        }
    \group_end:
  }

% ----------------------------------------------------------------------------

\xsim_declare_printsolutions_condition:nn {per-section}
  { \__xsim_if_solutions_per_section:nnnTF }
\xsim_declare_printsolutions_condition:nn {per-chapter}
  { \__xsim_if_solutions_per_chapter:nnnTF }
\xsim_declare_printsolutions_condition:nn {per-collection}
  { \__xsim_if_solutions_in_collection:nnnTF }

% ----------------------------------------------------------------------------

\keys_define:nn {xsim/print-solutions}
  {
    headings .bool_set:N = \l__xsim_printsolutions_headings_bool ,
    headings .initial:n  = true ,
    headings-template .tl_set:N  = \l__xsim_printsolutions_headings_template_tl ,
    headings-template .initial:n = default ,
    include-exercise  .bool_set:N = \l__xsim_include_question_bool ,
    include-exercise  .initial:n = false ,
    section           .choice: ,
    section/true      .code:n    =
      \__xsim_printsolutions_condition:nn  {per-section} {true}
      \int_set:Nn \l_xsim_printsolutions_section_int { \value {section} } ,
    section/false     .code:n    =
      \__xsim_printsolutions_condition:nn  {per-section} {false} ,
    section/unknown   .code:n    =
      \__xsim_printsolutions_condition:nn  {per-section} {true}
      \int_set:Nn \l_xsim_printsolutions_section_int {#1} ,
    section  .default:n  = true ,
    section  .initial:n  = false ,
    chapter           .choice: ,
    chapter/true      .code:n    =
      \__xsim_printsolutions_condition:nn  {per-chapter} {true}
      \int_set:Nn \l_xsim_printsolutions_chapter_int { \value {chapter} } ,
    chapter/false     .code:n    =
      \__xsim_printsolutions_condition:nn  {per-chapter} {false} ,
    chapter/unknown   .code:n    =
      \__xsim_printsolutions_condition:nn  {per-chapter} {true}
      \int_set:Nn \l_xsim_printsolutions_chapter_int {#1} ,
    chapter           .default:n  = true ,
    chapter           .initial:n  = false ,
    collection        .choice: ,
    collection/false  .code:n     =
      \__xsim_printsolutions_condition:nn  {per-collection} {false} ,
    collection/unknown .code:n    =
      \xsim_if_collection_exist:nT {#1}
        {
          \__xsim_printsolutions_condition:nn  {per-collection} {true}
          \tl_set:Nn \l__xsim_printsolutions_collection_tl {#1}
        } ,
    collection         .initial:n = false
  }

% ----------------------------------------------------------------------------
  
\file_input_stop:

2020/02/23 - resolve bug #56
