% !TEX encoding = UTF-8 Unicode
%
%% exercisepoints.sty
%% A LaTeX package to count exercises and points.
%%
%% Copyright (c) 2017-2019 Henning Kerstan.
%
% 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 Henning Kerstan.
%
% This work consists of the files `exercisepoints.sty' and `exercisepoints.tex'.
%


% package identification
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{exercisepoints}[%
  2019/01/03 v1.2.3 %
  count exercises and points%
]


% switches for custom exercise layout
\newif\if@exercisepoints@usecustomlayout%
\newif\if@exercisepoints@exercise@layoutdefined%
\newif\if@exercisepoints@subexercise@layoutdefined%

\DeclareOption{customlayout}{%
  \@exercisepoints@usecustomlayouttrue%
}

% this package does not have any other options
\DeclareOption*{
  \PackageWarning{exercisepoints}{Unknown option '\CurrentOption'}
}
\ProcessOptions\relax%


% avoid nesting (i.e. putting exercises in exercises)
\newif\if@exercisepoints@inexercise%
\global\@exercisepoints@inexercisefalse%
\newif\if@exercisepoints@insubexercise%
\global\@exercisepoints@insubexercisefalse%


% counters to store number of exercises
\newcounter{exercisepoints@exercisecount}
\newcounter{exercisepoints@subexercisecount}[exercisepoints@exercisecount]


% lengths to store points
\newlength{\exercisepoints@currentexercisepoints}
\newlength{\exercisepoints@currentsubexercisepoints}
\newlength{\exercisepoints@totalpoints}
\newlength{\exercisepoints@bonuspoints}


% macros to get total points and total number of exercises (whole document)
\providecommand{\totalpoints}{0}
\providecommand{\numberofexercises}{0}


% macros to get info about current exercise(-part)
% (to be used in AtBeginExercise or AtEndExercise hooks)
\providecommand{\currentexercisetitle}{}
\providecommand{\currentsubexercisetitle}{}

\providecommand{\currentexercisepoints}{%
  \if@exercisepoints@inexercise\else%
    \PackageError{exercisepoints}{'\string\currentexercisepoints' can only be used in exercise or subexercise environment}%
  \fi%
  %
  \ifcsname exercisepoints@points@\currentexercisenumber\endcsname%
    \@nameuse{exercisepoints@points@\currentexercisenumber}%
  \else%
    \textbf{??}% TODO:issue package warning in such a way that it can be used also in section commands
  \fi%
}

\providecommand{\currentsubexercisepoints}{%
  \if@exercisepoints@insubexercise\else%
    \PackageError{exercisepoints}{'\string\currentsubexercisepoints' can only be used in subexercise environment}%
  \fi%
  %
  \ifcsname exercisepoints@points@\currentexercisenumber.\currentsubexercisenumber\endcsname%
    \@nameuse{exercisepoints@points@\currentexercisenumber.\currentsubexercisenumber}%
  \else%
    \textbf{??}% TODO:issue package warning in such a way that it can be used also in section commands
  \fi%
}

\providecommand{\currentexercisenumber}{%
  \theexercisepoints@exercisecount%
}

\providecommand{\currentsubexercisenumber}{%
  \theexercisepoints@subexercisecount%
}


% hooks to modify the exercise typesetting
\providecommand{\AtBeginExercise}[1]{
  \renewcommand{\exercisepoints@begin}{#1}%
  \@exercisepoints@exercise@layoutdefinedtrue%
}

\providecommand{\AtEndExercise}[1]{
  \renewcommand{\exercisepoints@end}{#1}
}

% the same for subexercises
\providecommand{\AtBeginSubexercise}[1]{
  \renewcommand{\exercisepoints@sub@begin}{#1}
  \@exercisepoints@subexercise@layoutdefinedtrue%
}

\providecommand{\AtEndSubexercise}[1]{
  \renewcommand{\exercisepoints@sub@end}{#1}
}


% storage for the hooks 
\newcommand{\exercisepoints@begin}{}
\newcommand{\exercisepoints@end}{}
\newcommand{\exercisepoints@sub@begin}{}
\newcommand{\exercisepoints@sub@end}{}


% a very simple default exercise style
\if@exercisepoints@usecustomlayout\else%
  \RequirePackage{ifthen}%
  \AtBeginExercise{%
    ~\smallskip\\\sffamily%
    \ifthenelse{\equal{\currentexercisetitle}{}}%
      {\textbf{Exercise~\currentexercisenumber}} % empty title
      {%
        \textbf{Exercise~\currentexercisenumber:} %
    \currentexercisetitle%
      }% non-empty title
    \,~\hfill\textbf{(\currentexercisepoints~Points)}%
    \smallskip\\\noindent\normalfont%
  }
  \AtEndExercise{~\smallskip\\}
  %
  \AtBeginSubexercise{%
    ~\smallskip\\\sffamily%
    \ifthenelse{\equal{\currentsubexercisetitle}{}}%
      {\emph{Exercise~\currentexercisenumber.\currentsubexercisenumber}} % empty title
      {%
        \emph{Exercise~\currentexercisenumber.\currentsubexercisenumber:} %
    \emph{\currentsubexercisetitle}%
      }% non-empty title
    \,~\hfill\emph{(\currentsubexercisepoints~Points)}%
    \smallskip\\\noindent\normalfont%
  }
  \AtEndSubexercise{~\\}
\fi


% points commands sets points (additive within an exercise environment)
\newcommand{\points}[1]{%
  \if@exercisepoints@inexercise%
    \addtolength{\exercisepoints@currentexercisepoints}{#1 pt}%
    \global\exercisepoints@currentexercisepoints=\exercisepoints@currentexercisepoints%
    \addtolength{\exercisepoints@totalpoints}{#1 pt}%
    \global\exercisepoints@totalpoints=\exercisepoints@totalpoints%
    \if@exercisepoints@insubexercise%
      \addtolength{\exercisepoints@currentsubexercisepoints}{#1 pt}%
      \global\exercisepoints@currentsubexercisepoints=\exercisepoints@currentsubexercisepoints%
    \fi%
  \else%
    \PackageError{exercisepoints}{\string\points{...} can only be used within exercise or subexercise environment.}%
  \fi%
}

% itempoints units
\newcommand{\exercisepoints@itempointsunit@singular}{}
\newcommand{\exercisepoints@itempointsunit@plural}{}
\newcommand{\setitempointsunit}[2]{% singular, plural
  \renewcommand{\exercisepoints@itempointsunit@singular}{#1}%
  \renewcommand{\exercisepoints@itempointsunit@plural}{#2}%
}

% itempoints calls points and displays points flush right for use in
% enumerate environments
\RequirePackage{ifthen}
\newcommand{\itempoints}[1]{%
  \points{#1}%
  % check if exactly 1 points
  \ifthenelse{\equal{#1}{1}}{% not robust, so issuing 1.0 will not result in correct (singular) form!
    \ifthenelse{\equal{\exercisepoints@itempointsunit@singular}{}}{%
      \,~\hfill(#1)%
    }{%
      \,~\hfill(#1~\exercisepoints@itempointsunit@singular)%
    }%
  }{%
    \ifthenelse{\equal{\exercisepoints@itempointsunit@plural}{}}{%
      \,~\hfill(#1)%
    }{%
      \,~\hfill(#1~\exercisepoints@itempointsunit@plural)%
    }%
  }%
}




% getpoints retrieves points for a specific exercise number (starting at 0)
\newcommand{\getpoints}[1]{%
  \ifthenelse{#1 < \numberofexercises}%
  {\exercisepoints@readfromaux{points@#1}}%
  {\textbf{??}}%
}


% aux storage function (key-value store using #1 as key and #2 as value)
\newcommand{\exercisepoints@storetoaux}[2]{% 
  \immediate\write\@auxout{%
    \string\global\string\long\string\@namedef{exercisepoints@#1}{#2}%
  }%
}


% aux retrieval
\newcommand{\exercisepoints@readfromaux}[1]{%
  \ifcsname exercisepoints@#1\endcsname%
    \@nameuse{exercisepoints@#1}%
  \else%
    \textbf{??}%
    \PackageWarning{exercisepoints}{%
      Key 'exercisepoints@#1' not found in aux file. Maybe you need to recompile?%
    }%
  \fi%
}


% exercise environment; optional parameter is title of the respective exercise
\newenvironment{exercise}[1][]{%
  \if@exercisepoints@inexercise%
    \PackageError{exercisepoints}{You cannot nest exercise environments}%
  \fi
  %
  \if@exercisepoints@exercise@layoutdefined\else%
    \PackageError{exercisepoints}{Option `customlayout' requires you to define an exercise layout using at least `\string\AtBeginExercise{...}'}
  \fi 
  %
  \global\@exercisepoints@inexercisetrue%
  \setlength{\exercisepoints@currentexercisepoints}{0pt}%
  \global\exercisepoints@currentexercisepoints=\exercisepoints@currentexercisepoints%
  \renewcommand{\currentexercisetitle}{#1}%
  \exercisepoints@begin%
}{%
  \exercisepoints@end%
  \exercisepoints@storetoaux{points@\theexercisepoints@exercisecount}{\strip@pt\exercisepoints@currentexercisepoints}%
  \stepcounter{exercisepoints@exercisecount}%
  \global\@exercisepoints@inexercisefalse%
}


% exercise environment; optional parameter is title of the respective exercise
\newenvironment{subexercise}[1][]{%
  \if@exercisepoints@insubexercise%
    \PackageError{exercisepoints}{You cannot nest subexercise environments}%
  \fi%
  %
  \if@exercisepoints@subexercise@layoutdefined\else%
    \PackageError{exercisepoints}{Option `customlayout' requires you to define a subexercise layout using at least `\string\AtBeginSubexercise{...}'}
  \fi%
  \global\@exercisepoints@insubexercisetrue%
  \setlength{\exercisepoints@currentsubexercisepoints}{0pt}%
  \global\exercisepoints@currentsubexercisepoints=\exercisepoints@currentsubexercisepoints%
  \renewcommand{\currentsubexercisetitle}{#1}%
  \exercisepoints@sub@begin%
}{%
  \exercisepoints@sub@end%
  \exercisepoints@storetoaux{points@\theexercisepoints@exercisecount.\theexercisepoints@subexercisecount}{\strip@pt\exercisepoints@currentsubexercisepoints}%
  \stepcounter{exercisepoints@subexercisecount}%
  \global\@exercisepoints@insubexercisefalse%
}


\newcommand{\bonuspoints}[1]{%
  \setlength{\exercisepoints@bonuspoints}{#1 pt}%
  \global\exercisepoints@bonuspoints=\exercisepoints@bonuspoints%
}

\newcommand{\getbonuspoints}{%
  \exercisepoints@readfromaux{points@bonus}%
}

\newcommand{\totalpointswithbonus}{%
  \readfromaux{totalpointswithbonus}%
}


% read values from aux file at begin
\AtBeginDocument{
  \setlength{\exercisepoints@totalpoints}{0pt}% reset total points length
  %
  % set numberofexercises to 0 if not found in aux
  \ifcsname exercisepoints@numberofexercises\endcsname%
    \renewcommand{\numberofexercises}{%
      \exercisepoints@readfromaux{numberofexercises}%
    }%
  \fi%
  %
  \renewcommand{\totalpoints}{\exercisepoints@readfromaux{points@total}}%
  \renewcommand{\totalpointswithbonus}{\exercisepoints@readfromaux{points@totalwithbonus}}%
}


% store values in aux file at end
\AtEndDocument{
  \exercisepoints@storetoaux{numberofexercises}{\theexercisepoints@exercisecount}%
  \exercisepoints@storetoaux{points@total}{\strip@pt\exercisepoints@totalpoints}%
  \exercisepoints@storetoaux{points@bonus}{\strip@pt\exercisepoints@bonuspoints}%
  \newlength{\exercisepoints@totalpointswithbonus}%
  \setlength{\exercisepoints@totalpointswithbonus}{\exercisepoints@totalpoints}%
  \addtolength{\exercisepoints@totalpointswithbonus}{\exercisepoints@bonuspoints}%
  \exercisepoints@storetoaux{points@totalwithbonus}{\strip@pt\exercisepoints@totalpointswithbonus}%
}
