\def\fileversion{0.5a}
\def\filedate{2008/10/23}

% TODO: fix loading of babel before this package

%\NeedsTeXFormat{LaTeX2e}[1994/12/01]
\ProvidesPackage{exercisesheets}[\filedate\space\fileversion\space Typesetting of exercise sheets]
\message{`Typesetting of exercise sheets' \fileversion\space <\filedate>}

\RequirePackage{etoolbox}[2008/06/28]
\RequirePackage{scrlfile}

\RequirePackage{pgfkeys}
\RequirePackage{keyval}
\RequirePackage{kvoptions}

\RequirePackage{ifthen}

\RequirePackage{amsmath}

% configuration keys

\newif\ifexsh@solutions
\newif\ifexsh@nonoralsolutions\exsh@nonoralsolutionstrue
\pgfkeys{/exsh/.is family,/exsh,
  solutions/.is choice,
  solutions/true/.code=\exsh@solutionstrue\exsh@nonoralsolutionstrue,
  solutions/oral/.code=\exsh@solutionstrue\exsh@nonoralsolutionsfalse,
  solutions/false/.code=\exsh@solutionsfalse\exsh@nonoralsolutionsfalse,
  solutions/.default=true,
  only/.initial={-},
  all/.style={/exsh/only={-}},all/.value forbidden,
  solutionsby/.code={\def\exsh@solutionsby{#1}},solutionsby/.value required,
}

% store title data for sheet headers
\ifundef\subject{% \subject is only provided by KOMA classes
  \newcommand{\subject}[1]{}%
  \def\@subject{}%
}{}
\apptocmd{\subject}{\pgfkeys{/exsh/subject={#1}}}{}{}%
\ifdefempty{\@subject}{
  \pgfkeys{exsh,subject/.initial={\PackageWarning{exercisesheets}{No \string\subject\space given}}}
}{
  \pgfkeys{/exsh/subject/.initial/.expand once=\@subject}
}
\ifdefstring{\@date}{\today}{
  \pgfkeys{/exsh/semester/.initial={\PackageWarning{exercisesheets}{No semester given in \string\date}}}
}{
  \pgfkeys{/exsh/semester/.initial/.expand once=\@date}
}
\apptocmd{\date}{\pgfkeys{/exsh/semester={#1}}}{}{}
\def\exsh@patchauthor{%
  \patchcmd{\exsh@author}{\and}{, }{\exsh@patchauthor}{}%
}
\pgfkeys{/exsh/author/.code={%
    \def\exsh@author{#1}%
    \exsh@patchauthor
  },
  /exsh/author/.expand once=\@author}
\let\exsh@oldauthor\author
\def\author#1{\exsh@oldauthor{#1}\pgfkeys{/exsh/author={#1}}}

% helper macro for automatic language selection
\newif\ifexsh@provideonly
\def\exsh@deforprovide#1#2{%
  \ifthenelse{\(\NOT\boolean{exsh@provideonly}\)
    \OR \equal{}{\pgfkeysvalueof{#1}}}{%
    \pgfkeys{#1=#2}%
  }{}%
}

\pgfkeys{exsh,
  % additional sheet info
  title/.initial={},
  date/.initial={},
  note/.initial={},
  %
  % additional exercise info
  firstline/.initial={},
  points/.initial={},
  oral/.style={/exsh/points=oral},oral/.value forbidden,
  %
  % translatable strings
  strings/sheet/.initial={},
  strings/sheets/.initial={},
  strings/solutions/.initial={},
  strings/solutionsby/.initial={},
  strings/exercise/.initial={},
  strings/solution/.initial={},
  strings/hint/.initial={},
  strings/oral/.initial={},
  strings/point/.initial={},
  strings/points/.initial={},
  %
  % languages: predifined string sets
  language/.is choice,
  language/german/.code={%
    \exsh@deforprovide{/exsh/strings/sheet}      {\"Ubungsblatt}%
    \exsh@deforprovide{/exsh/strings/sheets}     {\"Ubungsbl\"atter}%
    \exsh@deforprovide{/exsh/strings/solutions}  {L\"osungsvorschl\"age}%
    \exsh@deforprovide{/exsh/strings/solutionsby}{L\"osungen von}%
    \exsh@deforprovide{/exsh/strings/exercise}   {Aufgabe}%
    \exsh@deforprovide{/exsh/strings/solution}   {L\"osung}%
    \exsh@deforprovide{/exsh/strings/hint}       {Hinweis}%
    \exsh@deforprovide{/exsh/strings/oral}       {m\"undlich}%
    \exsh@deforprovide{/exsh/strings/point}      {Punkt}%
    \exsh@deforprovide{/exsh/strings/points}     {Punkte}%
  },language/german/.value forbidden,
  language/ngerman/.style={/exsh/language/german=#1},
  language/english/.code={%
    \exsh@deforprovide{/exsh/strings/sheet}      {Exercise Sheet}%
    \exsh@deforprovide{/exsh/strings/sheets}     {Exercise Sheets}%
    \exsh@deforprovide{/exsh/strings/solutions}  {Suggested Solutions}%
    \exsh@deforprovide{/exsh/strings/solutionsby}{Solutions by}%
    \exsh@deforprovide{/exsh/strings/exercise}   {Exercise}%
    \exsh@deforprovide{/exsh/strings/solution}   {Solution}%
    \exsh@deforprovide{/exsh/strings/hint}       {Hint}%
    \exsh@deforprovide{/exsh/strings/oral}       {oral}%
    \exsh@deforprovide{/exsh/strings/point}      {point}%
    \exsh@deforprovide{/exsh/strings/points}     {points}%
  },language/english/.value forbidden,
  language/.unknown/.code={%
    \PackageWarning{exercisesheets}{Unknown language \pgfkeyscurrentname.
      Defaulting to English.}{}%
    \pgfkeys{/exsh/language=english}%
  },
  %
  % hooks
  every sheet/.code={},
  every exercise/.code={},
  every solution/.code={},
  every hint/.code={},
  %
  % font specifications
  sheet header font/.initial=\normalfont\normalsize,
  subject font/.initial=\scshape,
  author font/.initial=\scshape,
  semester font/.initial={},
  date font/.initial={},
  solutionsby font/.initial=\itshape,
  sheet note font/.initial=\itshape\bfseries,
  sheet title font/.initial=\Large\bfseries,
  exercise title font/.initial=\bfseries,
  points font/.initial=\itshape,
  hint font/.initial={},
  hint title font/.initial=\itshape,
  solution font/.initial={},
  solution title font/.initial=\bfseries,
  todo marker font/.initial=\ifdef{\color}{\color{red}}{}\bfseries,
  %
  % skip values
  above sheet title skip/.initial={4ex},
  above sheet note skip/.initial={.7ex},
  above solutionsby skip/.initial={1ex},
  below sheet header skip/.initial={4ex plus 1ex minus .5ex},
  above exercise skip/.initial={3ex plus 1ex minus .5em},
  below exercise title skip/.initial={0pt},
  above solution skip/.initial={1ex},
  above hint skip/.initial={1ex},
}

% default values
\pgfkeys{exsh,solutions=false,all}

% process package options
\@expandtwoargs\pgfqkeys{/exsh}{\csname opt@exercisesheets.sty\endcsname}

% mark options as processed
\expandafter\undef\csname opt@exercisesheets.sty\endcsname

% allow further keys to be set
\def\exshset#1{\pgfkeys{exsh,#1}}

% automatic language selection
\AfterEndPreamble{%
  \exsh@provideonlytrue
  \ifdef{\bbl@main@language}{%
    \pgfkeys{/exsh/language=\bbl@main@language}%
  }{%
    \pgfkeys{/exsh/language=english}%
  }%
  \exsh@provideonlyfalse
}%

% range checks
\newif\ifexsh@isinrange
\newif\ifexsh@singularrange
\def\exsh@checkrange#1{%
  \edef\exsh@range@entry{#1}%
  \def\exsh@range@curstart{}%
  \exsh@isinrangefalse
  \exsh@singularrangetrue
  \pgfkeysgetvalue{/exsh/only}{\exsh@temp}%
  \expandafter\exsh@range@parsestart\exsh@temp\exsh@range@end
}
\def\exsh@range@parsestart#1{%
  \ifstrequal{#1}{,}{%
    \exsh@singularrangefalse
    \ifdefstring{\exsh@range@curstart}{last}{%
      \let\exsh@range@curstart\exsh@lastsheet
    }{}%
    \ifdefequal{\exsh@range@entry}{\exsh@range@curstart}{%
      \exsh@isinrangetrue
    }{}%
    \def\exsh@range@curstart{}%
    \exsh@range@parsestart
  }{%
    \ifstrequal{#1}{\exsh@range@end}{%
      \ifdefstring{\exsh@range@curstart}{last}{%
        \let\exsh@range@curstart\exsh@lastsheet
      }{}%
      \ifdefequal{\exsh@range@entry}{\exsh@range@curstart}{%
        \exsh@isinrangetrue
      }{}%
    }{%
      \ifdefequal{#1}{-}{%
        \exsh@singularrangefalse
        \ifdefstring{\exsh@range@curstart}{}{%
          \def\exsh@range@curstart{0}%
        }{}%
        \ifdefstring{\exsh@range@curstart}{last}{%
          \let\exsh@range@curstart\exsh@lastsheet
        }{}%
        \def\exsh@range@curend{}%
        \exsh@range@parseend
      }{%
        \appto{\exsh@range@curstart}{#1}%
        \exsh@range@parsestart
      }%
    }%
  }%
}
\def\exsh@range@parseend#1{%
  \ifstrequal{#1}{,}{%
    \ifdefstring{\exsh@range@curend}{last}{%
      \def\exsh@range@curend{10000}%
    }{}%
    \ifdefstring{\exsh@range@curend}{}{
      \def\exsh@range@curend{10000}%
    }{}%
    \ifthenelse{\NOT \(\exsh@range@curstart > \exsh@range@entry
      \OR \exsh@range@entry > \exsh@range@curend\)}{%
      \exsh@isinrangetrue
    }{}%
    \def\exsh@range@curstart{}%
    \exsh@range@parsestart
  }{%
    \ifstrequal{#1}{\exsh@range@end}{%
      \ifdefstring{\exsh@range@curend}{last}{%
        \def\exsh@range@curend{10000}%
      }{}%
      \ifdefstring{\exsh@range@curend}{}{
        \def\exsh@range@curend{10000}%
      }{}%
      \ifthenelse{\NOT \(\exsh@range@curstart > \exsh@range@entry
        \OR \exsh@range@entry > \exsh@range@curend\)}{%
        \exsh@isinrangetrue
      }{%
      }%      
    }{%
      \appto{\exsh@range@curend}{#1}%
      \exsh@range@parseend
    }%
  }%
}

% remember last sheet (for range checks)
\def\exsh@lastsheet{0}% to be overridden in aux file
\AtEndDocument{%
  \immediate\write\@auxout{\string\gdef\string\exsh@lastsheet{\arabic{part}}}%
}

\numberwithin{page}{part}
\renewcommand{\thepage}{\arabic{page}}%changed for every sheet
\setcounter{secnumdepth}{-1}

% make PDF destinations unique:
\AfterPackage*{hyperref}{%
  \def\theHsection{\arabic{part}.\arabic{section}}%
  \def\theHpage{\arabic{part}-\arabic{page}}%
}

% automatic title selection
\let\exsh@title\relax
\let\exsh@title@new\relax
\def\exsh@write@title#1{%
  \xdef\exsh@title@new{\expandonce #1}%
  \protected@write\@auxout{}{\string\gdef\string\exsh@title{\expandonce\exsh@title@new}}%
}
\AfterEndDocument{%
  \ifthenelse{\NOT\equal{\exsh@title}{\exsh@title@new}}{%
    \@latex@warning@no@line{Label(s) may have changed.
      Rerun to get cross-references right}%
  }{%
%    \ifthenelse
  }%
}
\AfterEndPreamble{%
  \exsh@checkrange{0}%
  \ifexsh@singularrange\else
    \pgfkeysgetvalue{/exsh/strings/sheets}{\exsh@tmp}%
    \exsh@write@title{\exsh@tmp}%
  \fi
}

\AtBeginDocument{%
  \ifexsh@solutions
    \title{\exsh@title: \pgfkeysvalueof{/exsh/strings/solutions}}%
  \else
    \title{\exsh@title}
  \fi
}
\newenvironment{sheet}[1][]{%
  \clearpage
  \pgfkeys{exsh,every sheet,#1}%
  \ifthenelse{\equal{}{\pgfkeysvalueof{/exsh/title}}}{%
    \numdef\exsh@tmp{\value{part}+1}%
    \pgfkeys{/exsh/title/.expanded={\pgfkeysvalueof{/exsh/strings/sheet}\noexpand~\exsh@tmp}}%
    \def\thepage{\arabic{part}-\arabic{page}}%
  }{%
    \def\thepage{\arabic{page}}%
    \setcounter{section}{0}%
  }%
  \numdef\exsh@tmp{\value{part}+1}%
  \exsh@checkrange{\exsh@tmp}%
  \ifexsh@isinrange
    \ifexsh@singularrange
      \pgfkeysgetvalue{/exsh/strings/sheet}{\exsh@temp}%
      \eappto\exsh@temp{\noexpand~\expandonce\exsh@tmp}%
      \exsh@write@title{\exsh@temp}%
    \fi
    \exsh@sheethead
  \else
    \refstepcounter{part}%
    \expandafter\exsh@skipsheet
  \fi
}{%
  \par
  \begingroup
  \ifnum\value{page}=1\thispagestyle{empty}\fi
  \clearpage
  \endgroup
}
\long\def\exsh@skipsheet#1{%
  \ifstrequal{#1}{\begin}{\exsh@skipsheet@begin}{%
    \ifstrequal{#1}{\end}{\exsh@skipsheet@end}{%
      \ifstrequal{#1}{\label}{\exsh@skipsheet@label}{%
        \exsh@skipsheet}}}%
}
\def\exsh@skipsheet@end#1{%
  \ifstrequal{#1}{sheet}{\end{sheet}}{\exsh@skipsheet}%
}
\def\exsh@skipsheet@begin#1{%
  \ifstrequal{#1}{exercise}{\refstepcounter{section}}{}%
  \exsh@skipsheet%
}
\def\exsh@skipsheet@label#1{%
  \label{#1}\exsh@skipsheet
}

\def\exsh@sheethead{%
  \pgfkeysgetvalue{/exsh/title}{\exsh@tmp}%
  \ifexsh@solutions
    \expandafter\edef\expandafter\exsh@tmp{\expandonce\exsh@tmp:\noexpand~\pgfkeysvalueof{/exsh/strings/solutions}}%
  \fi
  \refstepcounter{part}%
  \stepcounter{page}%
  \part[\pgfkeysvalueof{/exsh/title}]{\normalsize
    \pgfkeysvalueof{/exsh/sheet header font}\centering%
    \begingroup\raggedright%
      {\pgfkeysvalueof{/exsh/subject font}\pgfkeysvalueof{/exsh/subject}}\hfill
      {\pgfkeysvalueof{/exsh/semester font}\pgfkeysvalueof{/exsh/semester}}\\
      {\pgfkeysvalueof{/exsh/author font}\exsh@author}\hfill
      {\pgfkeysvalueof{/exsh/date font}\pgfkeysvalueof{/exsh/date}}\\
      \vskip\pgfkeysvalueof{/exsh/above sheet title skip}\relax%
    \endgroup
    \begingroup
      \pgfkeysvalueof{/exsh/sheet title font}\exsh@tmp\\\unskip
    \endgroup
    \begingroup
      \pgfkeysvalueof{/exsh/sheet header font}%
      \ifexsh@solutions%
        \ifdef{\exsh@solutionsby}{%
          \begingroup
            \pgfkeysvalueof{/exsh/solutionsby font}%
            \vskip\pgfkeysvalueof{/exsh/above solutionsby skip}\relax
            \pgfkeysvalueof{/exsh/strings/solutionsby}~\exsh@solutionsby\\\unskip%
          \endgroup
        }{}%
      \else
      \fi
      \ifthenelse{\equal{}{\pgfkeysvalueof{/exsh/note}}}{%
      }{%
        \begingroup
          \pgfkeysvalueof{/exsh/sheet note font}%
            \vskip\pgfkeysvalueof{/exsh/above sheet note skip}\relax
          \pgfkeysvalueof{/exsh/note}\\\unskip%
        \endgroup
      }%
    \endgroup
  }%
  \unskip
  \vskip\pgfkeysvalueof{/exsh/below sheet header skip}\relax
}

\ifdef{\othersectionslevelsformat}{%
  \renewcommand{\othersectionlevelsformat}[1]{%
    \ifstrequal{#1}{section}%
    {}%
    {\csname the#1\endcsname\autodot\enskip}}%
}{}

\newenvironment*{exercise}[1][]{%
  \pgfkeys{exsh,every exercise,#1}%
  \ifthenelse{\equal{}{\pgfkeysvalueof{/exsh/points}}}{%
    \def\exsh@points{}%
    \def\exsh@shortpoints{}%
  }{%
    \ifthenelse{\equal{oral}{\pgfkeysvalueof{/exsh/points}}}{%
      \edef\exsh@points{\pgfkeysvalueof{/exsh/strings/oral}}%
    }{%
      \ifthenelse{\equal{1}{\pgfkeysvalueof{/exsh/points}}}{%
        \edef\exsh@points{1 \pgfkeysvalueof{/exsh/strings/point}}%
      }{%
        \edef\exsh@points{\pgfkeysvalueof{/exsh/points} \pgfkeysvalueof{/exsh/strings/points}}%
      }%
    }%
    \edef\exsh@shortpoints{ (\expandonce\exsh@points)}%
    \preto\exsh@points{\enskip\hfill\bgroup%
      \pgfkeysvalueof{/exsh/points font}}%
    \appto\exsh@points{\egroup}%
  }%
  \refstepcounter{section}%
  \ifthenelse{\equal{}{\pgfkeysvalueof{/exsh/firstline}}}{%
    \@startsection{section}{1}{\z@}%
    {\pgfkeysvalueof{/exsh/above exercise skip}}%
    {1em}%
    {\pgfkeysvalueof{/exsh/exercise title font}}%
    [\pgfkeysvalueof{/exsh/strings/exercise}~\arabic{section}\exsh@shortpoints]{\noindent%
      \pgfkeysvalueof{/exsh/strings/exercise}~\arabic{section}\exsh@points}%
  }{%
    \@startsection{section}{1}{\z@}%
    {\pgfkeysvalueof{/exsh/above exercise skip}}%
    {1em}%
    {\pgfkeysvalueof{/exsh/exercise title font}}%
    [\pgfkeysvalueof{/exsh/strings/exercise}~\arabic{section}\exsh@shortpoints]{\noindent%
      \pgfkeysvalueof{/exsh/strings/exercise}~\arabic{section}\quad%
      {\normalfont\normalsize\pgfkeysvalueof{/exsh/firstline}}\exsh@points}%
  }%
  \unskip\vskip\pgfkeysvalueof{/exsh/below exercise title skip}\relax
  \@afterindentfalse
  \ignorespaces
}{}

\AfterPackage*{varioref}{
  \labelformat{section}{\pgfkeysvalueof{/exsh/strings/exercise}~\arabic{section}}
  \labelformat{enumi}{\arabic{section}.#1}
}
\AfterPackage*{paralist}{
  \setdefaultenum{(a)}{(1)}{i.}{A.}
}

\newenvironment{hint}[1][]{%
  \pgfkeys{exsh,every hint,#1,hint font}%
  \par\vskip\pgfkeysvalueof{/exsh/above hint skip}\relax\noindent
  {\pgfkeysvalueof{/exsh/hint title font}\pgfkeysvalueof{/exsh/strings/hint}:}~\ignorespaces
}{%
  \par
}
\newenvironment{hint*}[1][]{%
  \pgfkeys{exsh,every hint,#1,hint font}%
  ({\pgfkeysvalueof{/exsh/hint title font}\pgfkeysvalueof{/exsh/strings/hint}:}%
}{%
  \unskip)%
}
\newenvironment{solution}[1][]{%
  \pgfkeys{exsh,every solution,#1,solution font}%
  \ifbool{exsh@solutions}{%
    \ifbool{exsh@nonoralsolutions}{%
      \let\next\exsh@startsolution
    }{%
      \ifthenelse{\equal{oral}{\pgfkeysvalueof{/exsh/points}}}{%
        \let\next\exsh@startsolution
      }{%
        \let\next\exsh@skipsolution
      }%
    }%
  }{%
    \let\next\next\exsh@skipsolution
  }%
  \next
}{}

\def\exsh@startsolution{\@afterindentfalse
  \vskip\pgfkeysvalueof{/exsh/above solution skip}\relax%
  {\parindent \z@
    \pgfkeysvalueof{/exsh/solution title font}\nobreak
    \pgfkeysvalueof{/exsh/strings/solution}:\par\nobreak}\nobreak%
  \@afterheading
}%

\long\def\exsh@skipsolution#1{%
  \ifstrequal{#1}{\end}{\exsh@endsolution}{\exsh@skipsolution}%
}
\def\exsh@endsolution#1{%
  \ifstrequal{#1}{solution}{\end{solution}}{\exsh@skipsolution}%
}


\newcommand{\pun}[1][]{\space\hspace*{\fill}{\pgfkeysvalueof{/exsh/points font}\mbox{(#1)}}}

\newcommand{\TODO}{%
  {\pgfkeysvalueof{/exsh/todo marker font}TODO}%
  \ifexsh@solutions
    \PackageWarning{exercisesheets}%
    {TODO marker found}%
  \else
    \PackageError{exercisesheets}%
    {TODO marker found}%
  \fi
}

\newcommand{\ifsolutions}[2]{\ifexsh@solutions #1\else #2\fi}
