% \iffalse meta-comment
%
% Copyright (C) 2019 by Benjamin Berg <benjamin@sipsolutions.net>
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% 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
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is Benjamin Berg.
%
% \fi
%
% \iffalse
%<*driver>
\ProvidesFile{sdapsclassic.dtx}
%</driver>
%<class>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%<class>\ProvidesClass{sdapsclassic}
%<*class>
    [2015/08/02 v0.1 Initial version of SDAPS classic class]
%</class>
%
%<*driver>
\documentclass{ltxdoc}
%\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{sdapsclassic.dtx}
\end{document}
%</driver>
% \fi
%
% \CheckSum{0}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
%
% \changes{v0.1}{2015/01/14}{Initial version}
%
% \GetFileInfo{sdapsclassic.dtx}
%
% \DoNotIndex{\newcommand,\newenvironment}
% 
%
% \title{The \textsf{sdapsclassic} package\thanks{This document
%   corresponds to \textsf{sdapsclassic}~\fileversion, dated \filedate.}}
% \author{Benjamin Berg \\ \texttt{benjamin@sipsolutions.net}}
%
% \maketitle
%
% \section{Documentation}
%
% Please refer to \url{https://sdaps.org/class-doc} for documentation.
%
% \StopEventually{\PrintChanges\PrintIndex}
%
% \section{Implementation}
%
% This package uses the \LaTeX3 language internally, so we need to enable it.
%    \begin{macrocode}
% We need at least 2011-08-23 for \keys_set_known:nnN
\RequirePackage{expl3}[2011/08/23]
%\RequirePackage{xparse}
\ExplSyntaxOn

\RequirePackage{sdapsbase}
\RequirePackage{sdapslayout}

% We use verbatim to grab the content of the questionnaire envorinment into a
% temporary file. This way we can input it multiple times for repeating the
% the content.

\RequirePackage{verbatim}


%    \end{macrocode}
% Now a lot of LaTeX 2e stuff that is basically just a copy of the old class.
% Would be nicer to redo this with xparse, but what the heck.
%    \begin{macrocode}

\RequirePackage{scrkbase}


%-------------------------------------------------------------------------------
% option processing
%-------------------------------------------------------------------------------

% Whether the main SDAPS program will be used
\DeclareOption{disable_recognition}{\bool_gset_false:N\g_sdaps_recognition_bool}

% Whether sdaps should print the questionnaire id
\DeclareOption{no_print_questionnaire_id}{\bool_gset_false:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}}
\DeclareOption{print_questionnaire_id}{\bool_gset_true:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{NONE}}}

% pass unknown options to scrartcl
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}}

% We need this if right now as it is set by sdaps.opt.
\newif\if@sdaps@draft\@sdaps@drafttrue
\DeclareOption{final}{\@sdaps@draftfalse}

\tl_gset:Nn\g_sdaps_style_tl{qr}%


\DefineFamily{SDAPS}
\DefineFamilyMember{SDAPS}
\DefineFamilyKey{SDAPS}{sdaps_style}[qr]%
{%
  \KOMA@set@ncmdkey{sdaps_style}{@tempa}{%
    {code128}{0},%
    {custom}{1},%
    {qr}{2}%
  }{#1}%
  \ifcase \@tempa\relax
    \tl_gset:Nn\g_sdaps_style_tl{code128}%
  \or
    \tl_gset:Nn\g_sdaps_style_tl{custom}%
  \or
    \tl_gset:Nn\g_sdaps_style_tl{qr}%
  \fi
}

\DefineFamilyKey{SDAPS}{twoside_barcode}[both]%
{%
  \KOMA@set@ncmdkey{twoside_barcode}{@tempa}{%
    {both}{0},%
    {front}{1},%
    {back}{2}%
  }{#1}%
  \ifcase \@tempa\relax
    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{both}%
  \or
    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{front}%
  \or
    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{back}%
  \fi
}

\DefineFamilyKey{SDAPS}{checkmode}[checkcorrect]%
{%
  \KOMA@set@ncmdkey{checkmode}{@tempa}{%
    {checkcorrect}{0},%
    {check}{1},%
    {fill}{2}%
  }{#1}%
  \ifcase \@tempa\relax
    \tl_gset:Nn\g_sdaps_checkmode_tl{checkcorrect}%
  \or
    \tl_gset:Nn\g_sdaps_checkmode_tl{check}%
  \or
    \tl_gset:Nn\g_sdaps_checkmode_tl{fill}%
  \fi
}


\DefineFamilyKey{SDAPS}{globalid}[]
{
  \tl_gset:Nn \g_sdaps_global_id_tl { #1 }
}

\DefineFamilyKey{SDAPS}{globalidlabel}[]
{
  \tl_gset:Nn \g_sdaps_global_id_label_tl { #1 }
}

% Set default options for scrartcl. Is this done correctly?
\PassOptionsToClass{headings=small}{scrartcl}
\PassOptionsToClass{twoside}{scrartcl}
% Well, I don't think that this worked in the past ...
%\PassOptionsToClass{10pt}{scrartcl}

\seq_new:N \g_sdaps_questionnaire_ids_seq
\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}

% process given options
\FamilyProcessOptions{SDAPS}\relax

%-------------------------------------------------------------------------------
% load base-class
%-------------------------------------------------------------------------------
\LoadClass{scrartcl}


%-------------------------------------------------------------------------------
% required packages
%-------------------------------------------------------------------------------
% geometry package
\RequirePackage{geometry}
% Corner mark postions plus 2mm, except for the header
\geometry{top=12mm}
\geometry{bottom=14mm}
\geometry{hmargin=12mm}
\geometry{includeheadfoot}
\geometry{headheight=\baselineskip}
\geometry{headsep=\baselineskip}
% QR code size plus 2mm
\geometry{footskip=12mm}

% ifthen package
\RequirePackage{ifthen}

% fontenc package
\RequirePackage[T1]{fontenc}

% color
\RequirePackage{color}

% Symbols (boxes)
\RequirePackage{amssymb}

% For writing out the page number of boxes
\RequirePackage{refcount}

% Defines the LastPage label
\RequirePackage{lastpage}

% Environment creation that gets the body as a macro
\RequirePackage{environ}

% headers and footers
\usepackage{scrlayer-scrpage}
\clearpairofpagestyles
\chead*{\@title}
\cfoot*{\sdapspagemark}

% hyperrefs
\RequirePackage{url}
\RequirePackage{hyperref}
\hypersetup{%
  breaklinks,%
  baseurl       = http://,%
  pdfborder     = 0 0 0,%
  pdfpagemode   = UseNone,%
  pdfcreator    = \LaTeX{} with `sdaps' class,%
  pdfproducer   = \LaTeX{}
}

% graphics
\RequirePackage{graphicx}

% Section formatting
\RequirePackage{sectsty}

% table of fixed width
\RequirePackage{tabularx}

% Babel (needed for the translator)
\RequirePackage{babel}

% Translation
\RequirePackage{translator}
\usedictionary{translator-sdaps-dictionary}


% Execute sdaps.opt file to allow SDAPS to override any options
\InputIfFileExists{sdaps.opt}{}{}


%-------------------------------------------------------------------------------
%                class definition
%-------------------------------------------------------------------------------
% minimal base settings

\setlength\lineskip{1\p@}
\setlength\normallineskip{1\p@}
\renewcommand\baselinestretch{}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1.0ex \@plus 1.5ex \@minus -0.25ex}
%\setlength{\parskip}{0pt}
\setlength\columnsep{10\p@}
\setlength\columnseprule{0\p@}
% Redefine the spacings for \subsection ie. \_sdaps_classic_question.
\renewcommand\section{\@startsection {section}{1}{\z@}%
      {-\parskip}%
      {\parskip}%
      {\normalfont\Large\bfseries\SS@sectfont}}
\renewcommand\subsection{
  \@startsection{subsection}{2}{\z@}%
      {0.5\parskip}%
      {0.25\parskip}% These are deleted again for questions
      {}%
}
\pagestyle{scrheadings}
\pagenumbering{arabic}
\raggedbottom
%\flushbottom
\onecolumn

%
% Fonts for the "classic" class
%

%common font settings
\newkomafont{questionfont}{}
\newkomafont{choicefont}{}
%singlemark
\newkomafont{singlemarkquestionfont}{\usekomafont{questionfont}}
\newkomafont{singlemarkchoicefont}{\usekomafont{choicefont}}
%markgroup
\newkomafont{marklinequestionfont}{\usekomafont{questionfont}}
\newkomafont{marklinechoicefont}{\usekomafont{choicefont}}
%choicequestion
\newkomafont{choiceitemfont}{\usekomafont{choicefont}}
%choicegroup
\newkomafont{choicegroupchoicefont}{\usekomafont{questionfont}}
\newkomafont{choicegrouplinefont}{\usekomafont{choicefont}}


%    \end{macrocode}
%
% \subsection{A bunch of old commands}
%
%
%    \begin{macrocode}


\providecommand{\addinfo}[2]{
  \sdaps_info_write:x{\unexpanded{#1}=\unexpanded{#2}}
}

\newcommand\qid{\tl_use:N \g__sdaps_questionnaire_id_tl}

\def\_sdaps_classic_question#1{%
  \tl_if_empty:nTF{#1}{
    \refstepcounter{subsection}%
    \par%
  } {
    % #1 is nonempty
    \subsection{\usekomafont{questionfont}\strut\ignorespaces#1}%
    % This is extra spacing after the subsection is removed. By doing this we
    % get exactly one \parskip
    \nobreak%
    \vspace{-0.25\parskip}%
    \nobreak%
  }
}

\newenvironment{info}{%
  \group_begin:
    {
      \par
      % Prevent top skip glue from being inserted at the start of the page
      \topskip=0pt
      \noindent\hrule height 1pt%
      \nobreak
      \vspace{-\parskip}
      \nobreak
      \vspace{0.5ex}
      \nobreak
    }
}{%
    \par
    \nobreak
    \vspace{-\parskip}
    \nobreak
    \vspace{0.5ex}
    \nobreak
    \noindent\hrule height 1pt
  \group_end:
}


\definecolor{sectionbgcolor}{gray}{0.8}
\definecolor{sectionfgcolor}{gray}{0.0}

\newcommand{\sectbox}[1]{%
 \noindent\protect\colorbox{sectionbgcolor}{%
   \@tempdima=\hsize
   \advance\@tempdima by-2\fboxsep
   \protect\parbox{\@tempdima}{%
     \smallskip
     \raggedright % extra commands here
     \color{sectionfgcolor}\usekomafont{section}{#1} \smallskip
    }%
  }%
}

\sectionfont{\sectbox}

\setkomafont{disposition}{\normalfont}
\addtokomafont{section}{\bfseries\sffamily}





% Not sure why we even had this ...
\def\smallskip{\vspace\smallskipamount}
\def\medskip{\vspace\medskipamount}
\def\bigskip{\vspace\bigskipamount}
\newskip\smallskipamount \smallskipamount=3pt  plus 1pt minus 1pt
\newskip\medskipamount   \medskipamount  =6pt  plus 2pt minus 2pt
\newskip\bigskipamount   \bigskipamount  =12pt plus 4pt minus 4pt



\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { nVVnn }
\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { VVVnn }
\cs_generate_variant:Nn \int_set:Nn { NV }


%    \end{macrocode}
%
% \subsection{The different question environments}
%
%
%    \begin{macrocode}


\bool_new:N \g__sdaps_classic_have_section
\bool_gset_false:N \g__sdaps_classic_have_section
\cs_new_eq:NN\_sdapsclassic_origsection\section
\renewcommand{\section}[1]{
  \bool_if:NT \g__sdaps_classic_have_section {
    \sdaps_qobject_end:n { section }
  }

  \_sdapsclassic_origsection{#1}
  \bool_gset_true:N \g__sdaps_classic_have_section
  \sdaps_qobject_begin:nnn { section }{ Head }{ #1 }
}

\cs_new_protected_nopar:Nn \sdaps_classic_ensure_section: {
  \bool_if:NF \g__sdaps_classic_have_section {
    % This is a bad hack to make the numbering start with zero
    \int_gdecr:N \g__sdaps_object_id_int
    \bool_gset_true:N \g__sdaps_classic_have_section
    \sdaps_qobject_begin:nnn { section }{ Head }{ }
      \sdaps_context_hook_end:n { \bool_gset_false:N \g__sdaps_classic_have_section }
  }
}


\tl_new:N \l_sdaps_classic_textbox_var_tl
\tl_new:N \l_sdaps_classic_textbox_text_tl
\keys_define:nn { sdapsclassic / textbox }
{
  var        .tl_set:N   = \l_sdaps_classic_textbox_var_tl,
  text       .tl_set:N   = \l_sdaps_classic_textbox_text_tl,
}

% With star non-expanding, without start expanding
\providecommand{\textbox}{\@ifstar
    {\bool_set_false:N \l_tmpa_bool \_sdaps_classic_textbox }
    {\bool_set_true:N \l_tmpa_bool \_sdaps_classic_textbox }
}

\providecommand{\_sdaps_classic_textbox}[3][]
{
  \keys_set:nn { sdapsclassic / textbox } { #1 }

  \sdaps_classic_ensure_section:
  \_sdaps_classic_question{#3}

  \tl_if_empty:NTF \l_sdaps_classic_textbox_text_tl {
    \sdaps_qobject_begin:nnn { textbox } { Text } { #3 }
  } {
    \sdaps_qobject_begin:nnV { textbox } { Text } \l_sdaps_classic_textbox_text_tl
  }

  \bool_if:NTF \l_tmpa_bool {
    \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 1 }
  }{
    \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 0 }
  }

  \sdaps_qobject_end:n { textbox }
}

\newcounter{markcheckboxcount}
\setcounter{markcheckboxcount}{5}


\tl_new:N \l_sdaps_singlemark_var_tl
\int_new:N \l_sdaps_singlemark_count_int

\keys_define:nn { sdaps / singlemark }
{
  var        .tl_set:N   = \l_sdaps_singlemark_var_tl,

  % 0 is equivalent to using the markcheckboxcount counter
  count      .int_set:N  = \l_sdaps_singlemark_count_int,
  count      .initial:n  = 0,
}

\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_prepare:NN
{
  \group_begin:
    \sdaps_context_begin_local:
    \sdaps_context_disable_writing:
    \bool_if:NT #1 {
      \sdaps_context_append:nn { singlechoice } { draw_check=true }
      \sdaps_context_append:nn { multichoice } { draw_check=true }
    }
    \bool_if:NT #2 {
      \sdaps_context_append:nn { singlechoice } { fill=black }
      \sdaps_context_append:nn { multichoice } { fill=black }
    }
}

\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_single:
{
    \sdaps_checkbox_set_type:n { singlechoice }
    \sdaps_checkbox:nn { } { }
  \group_end:
  \ignorespaces
}

\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_multi:
{
    \sdaps_checkbox_set_type:n { multichoice }
    \sdaps_checkbox:nn { } { }
  \group_end:
  \ignorespaces
}

\providecommand{\checkbox}{
  \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_false_bool
  \@ifstar
    \_sdaps_classic_dummy_checkbox_single:
    \_sdaps_classic_dummy_checkbox_multi:
}
\providecommand{\checkedbox}{%
  \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_false_bool
  \@ifstar
    \_sdaps_classic_dummy_checkbox_single:
    \_sdaps_classic_dummy_checkbox_multi:
}
\providecommand{\filledbox}{%
  \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_true_bool
  \@ifstar
    \_sdaps_classic_dummy_checkbox_single:
    \_sdaps_classic_dummy_checkbox_multi:
}
\providecommand{\correctedbox}{%
  \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_true_bool
  \@ifstar
    \_sdaps_classic_dummy_checkbox_single:
    \_sdaps_classic_dummy_checkbox_multi:
}

\providecommand*{\singlemark}[4][]{%
  \sdaps_classic_ensure_section:

  \group_begin:

  \keys_set:nn { sdaps / singlemark } { #1 }

  \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } {
    \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount }
  }

  \_sdaps_classic_question{#2}%

  \sdaps_qobject_begin:nnn { singlemark } { range } { #2 }

  \sdaps_checkbox_set_type:n { singlechoice }

  \tl_if_empty:NF \l_sdaps_singlemark_var_tl {
    \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl
  }

  \sdaps_range:nnn{lower}{0}{#3}
  \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4}

  \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}X}
    {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} &
      \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & }
    {\usekomafont{singlemarkchoicefont}#4\hfill}\\%
  \end{tabularx}%

  \sdaps_qobject_end:n { singlemark }
  \group_end:
}


\providecommand*{\singlemarkother}[5][]{%
  \sdaps_classic_ensure_section:

  \group_begin:

  \keys_set:nn { sdaps / singlemark } { #1 }

  \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } {
    \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount }
  }

  \_sdaps_classic_question{#2}%

  \sdaps_qobject_begin:nnn { singlemark } { range } { #2 }

  \sdaps_checkbox_set_type:n { singlechoice }

  \tl_if_empty:NF \l_sdaps_singlemark_var_tl {
    \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl
  }

  \sdaps_range:nnn{lower}{0}{#3}
  \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4}
  \sdaps_answer:n{#5}

  \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}XX}
    {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} &
      \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & }
    {\usekomafont{singlemarkchoicefont}#4} &
    {\usekomafont{singlemarkchoicefont}\sdaps_checkbox:nn { _0 } { 0 } {} ~ #5\hfill}\\%
  \end{tabularx}%

  \sdaps_qobject_end:n { singlemark }
  \group_end:
}

\dim_new:N \l__sdaps_classic_choiceitem_pad_dim
\int_new:N \l__sdaps_classic_choiceitem_cols_int
\int_new:N \l__sdaps_classic_choiceitem_col_int
\dim_new:N \l__sdaps_classic_choicequestion_prevdepth_dim
\coffin_new:N \l__sdaps_classic_choicequestion_coffin

\msg_new:nnn { sdapsclassic } { choicequestion_wrong_mode } { Mode~should~always~be~vertical~inside~a~choicequestion.\\ This~likely~means~that~the~choicequestion~contains~content~other~than~one~of~the~permissable~macros. }

\msg_new:nnn { sdapsclassic } { choicequestion_no_text } { Textboxes~cannot~be~used~in~singlechoice~questions.\\
It~is~currently~not~supported~to~use~text~answers~in~single~choice~questions.~Until~support~is~added~
you~can~get~similar~results~by~temporarily~changing~the~checkbox~style. }

\msg_new:nnn { sdapsclassic } { choicequestion_unknown_key } { The~key~'#1'~is~unknown.\\If~you~are~migrating~from~the~old~class~then~you~need~to~add~'cols='~to~specify~the~number~of~columns. }

\tl_new:N \l_sdaps_choicquestion_type_tl
\tl_new:N \l_sdaps_choicequestion_var_tl
\tl_new:N \l_sdaps_choicequestion_text_tl
\int_new:N \l_sdaps_choicequestion_cols_int

\keys_define:nn { sdaps / choicequestion }
{
  var        .tl_set:N   = \l_sdaps_choicequestion_var_tl,
  text       .tl_set:N   = \l_sdaps_choicequestion_text_tl,
  colsep     .dim_set:N  = \l_sdaps_choicequestion_colsep_dim,
  colsep     .initial:n  = 6pt,
  rowsep     .dim_set:N  = \l_sdaps_choicequestion_rowsep_dim,
  rowsep     .initial:n  = 0pt,
  cols       .int_set:N  = \l_sdaps_choicequestion_cols_int,
  cols       .initial:n  = 3,

  type       .choices:nn  = { multichoice, singlechoice } { \tl_set:Nx \l_sdaps_choicquestion_type_tl { \l_keys_choice_tl } },
  type       .initial:n   = { multichoice },

  singlechoice .meta:n    = { type=singlechoice },
  multichoice  .meta:n    = { type=multichoice },

  unknown .code:n = \msg_error:nnV { sdapsclassic } { choicequestion_unknown_key } \l_keys_key_tl
}


\tl_new:N \l_sdaps_choicequestion_choice_var_tl
\tl_new:N \l_sdaps_choicequestion_choice_val_tl
\tl_new:N \l_sdaps_choicequestion_choice_text_tl

\keys_define:nn { sdaps / choicequestion / choice }
{
  var        .tl_set:N   = \l_sdaps_choicequestion_choice_var_tl,
  val        .tl_set:N   = \l_sdaps_choicequestion_choice_val_tl,
  text       .tl_set:N   = \l_sdaps_choicequestion_choice_text_tl,
}


\newenvironment{choicequestion}[2][]{
  \group_begin:

  \sdaps_classic_ensure_section:

  \tl_clear:N \l_sdaps_choicequestion_var_tl
  \tl_clear:N \l_sdaps_choicequestion_text_tl

  \keys_set:nn { sdaps / choicequestion } { #1 }

  \_sdaps_classic_question{#2}%

  % Setup the context
  \tl_if_eq:VnTF \l_sdaps_choicquestion_type_tl { multichoice } {
    \tl_set:Nn \l_tmpa_tl { Choice }
  } {
    \tl_set:Nn \l_tmpa_tl { Option }
  }
  \tl_if_empty:NTF \l_sdaps_choicequestion_text_tl {
    \sdaps_qobject_begin:nVn { choicequestion } \l_tmpa_tl { #2 }
  } {
    \sdaps_qobject_begin:nVV { choicequestion } \l_tmpa_tl \l_sdaps_choicequestion_text_tl
  }

  \sdaps_checkbox_set_type:V \l_sdaps_choicquestion_type_tl

  \tl_if_empty:NF \l_sdaps_choicequestion_var_tl {
    \sdaps_qobject_append_var:V \l_sdaps_choicequestion_var_tl
  }

  \dim_set:Nn \l__sdaps_classic_choiceitem_pad_dim { 1ex }

  \int_set:NV \l__sdaps_classic_choiceitem_cols_int \l_sdaps_choicequestion_cols_int
  \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 }
  \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim { 1000pt }
  \coffin_clear:N \l__sdaps_classic_choicequestion_coffin

  % We have to be in vertical mode at this point.
  \if_mode_vertical:
    % Nothing
  \else:
    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
  \fi:

  % It is important to look like a paragraph, otherwise latex thinks there is
  % nothing between the two question sections and doesn't insert a voluntary
  % page break.
  \the\everypar

  % Paragraph like spacing
  \vspace{\parskip}

  \def\choicequestion_clubpenalty{\penalty\clubpenalty\def\choicequestion_clubpenalty{\relax}\def\choicequestion_clubpenalty{\penalty\widowpenalty}}
  \def\choicequestion_widowpenalty{}
}{
  \if_mode_vertical:
    % Nothing
  \else:
    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
  \fi:

  \choicequestion_widowpenalty
  \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin }
  \prevdepth=\l__sdaps_classic_choicequestion_prevdepth_dim
  \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
  \sdaps_qobject_end:n { choicequestion }

  \group_end:
  \vspace{\parskip}
}

\newenvironment{optionquestion}[2][]{
  \choicequestion[singlechoice,#1]{#2}
} {
  \endchoicequestion
}

\cs_new_protected_nopar:Nn \_sdaps_classic_line_shipout_add:NNn
{
  % We have to be in vertical mode at this point.
  \if_mode_vertical:
    % Nothing
  \else:
    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
  \fi:

  % Is linewidth the right thing here?
  \int_compare:nT { \l__sdaps_classic_choiceitem_col_int + #3 > \l__sdaps_classic_choiceitem_cols_int } {
    % We can only typeset a coffin in vertical mode if we use the box function
    \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin }
    \prevdepth=\l__sdaps_classic_choicequestion_prevdepth_dim
    \skip_vertical:N \l_sdaps_choicequestion_rowsep_dim
    \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
    \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim { 1000pt }
    \choicequestion_clubpenalty
    \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 }
  }

  \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
  \dim_set:Nn \l_tmpa_dim { \l__sdaps_classic_choiceitem_col_int \l_tmpa_dim }

  \dim_compare:nNnTF { \coffin_dp:N \l__sdaps_classic_choicequestion_coffin } > { \coffin_dp:N #1 } {
    \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim {\dim_min:nn { \l__sdaps_classic_choicequestion_prevdepth_dim } { #2 + \coffin_dp:N \l__sdaps_classic_choicequestion_coffin - \coffin_dp:N #1 } }
  } {
    \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim {\dim_min:nn { \l__sdaps_classic_choicequestion_prevdepth_dim + \coffin_dp:N #1 - \coffin_dp:N \l__sdaps_classic_choicequestion_coffin } { #2 } }
  }

  \coffin_join:NnnNnnnn \l__sdaps_classic_choicequestion_coffin { l } { H } #1 { l } { H } { \l_tmpa_dim } { 0pt }
  \int_add:Nn \l__sdaps_classic_choiceitem_col_int { #3 }
}

\providecommand*{\choiceitem}[2][]{%
  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl

  \keys_set:nn { sdaps / choicequestion / choice } { #1 }

  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
   \sdaps_answer:n { #2 }
  } {
   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
  }

  \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }

  \hcoffin_set:Nn \l_tmpa_coffin {
    \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\  }
    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
    \hskip \l_sdaps_choicequestion_colsep_dim
    \hbox_unpack:N \l_tmpa_box
    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}}
      \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #2 }\strut
      \par
      \dim_gset:Nn \g_tmpa_dim { \dim_max:nn { \box_dp:N \l_tmpa_box } { \prevdepth } }
      \unskip
    \end{minipage}
  }

  \_sdaps_classic_line_shipout_add:NNn \l_tmpa_coffin \g_tmpa_dim { 1 }
  \ignorespaces
}

\providecommand*{\choicemulticolitem}[3][]{ %
  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl

  \keys_set:nn { sdaps / choicequestion / choice } { #1 }

  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
   \sdaps_answer:n { #3 }
  } {
   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
  }

  \dim_set:Nn \l_tmpa_dim { #2\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #2 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }

  \hcoffin_set:Nn \l_tmpa_coffin {
    \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\  }
    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
    \hskip \l_sdaps_choicequestion_colsep_dim
    \hbox_unpack:N \l_tmpa_box
    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}}
      \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #3 }\strut
      \par
      \dim_gset:Nn \g_tmpa_dim { \dim_max:nn { \box_dp:N \l_tmpa_box } { \prevdepth } }
      \unskip
    \end{minipage}
  }

  \_sdaps_classic_line_shipout_add:NNn \l_tmpa_coffin \g_tmpa_dim { #2 }
  \ignorespaces
}

\providecommand{\choiceitemtext}[4][]{%
  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl

  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
  % TODO: Warn if val has been set here

  % If not multichoice then warn
  \tl_if_eq:VnF \l_sdaps_choicquestion_type_tl { multichoice } {
    \msg_error:nn { sdapsclassic } { choicequestion_no_text }
  }

  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
   \sdaps_answer:n { #4 }
  } {
   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
  }

  \dim_set:Nn \l_tmpa_dim { #3\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #3 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }

  \hcoffin_set:Nn \l_tmpa_coffin {
    \hskip \l_sdaps_choicequestion_colsep_dim
    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - 2\l_sdaps_choicequestion_colsep_dim}}
      \usekomafont{choiceitemfont}
      \dim_set:Nn \l_tmpa_dim { #2 }
      \dim_set:Nn \l_tmpb_dim { #2 }
      \dim_set:Nn \l_tmpa_dim { 0.5 \l_tmpa_dim - 0.8ex }
      \dim_set:Nn \l_tmpb_dim { 0.5 \l_tmpb_dim + 0.8ex }
      \strut\ignorespaces \tl_trim_spaces:n { #4 } ~
      \sdaps_textbox_hstretch:VVVnn \l_sdaps_choicequestion_choice_var_tl \l_tmpa_dim \l_tmpb_dim { 0pt } { 1 }
      \par
      \dim_gset:Nn \g_tmpa_dim { \prevdepth }
      \unskip
    \end{minipage}
  }

  \_sdaps_classic_line_shipout_add:NNn \l_tmpa_coffin \g_tmpa_dim { #3 }
  \ignorespaces
}



%    \end{macrocode}
%
% \subsection{The questionnaire environment}
%
% There is a bit of magic here. If (and only if) we need to render multiple
% pages of the questionnaire, then we write the whole questionnaire into a
% temporary file. This way we can input the file multiple times, which means
% that environments changing the parser (e.g. verbatim) work properly.
%
%    \begin{macrocode}
%

\iow_new:N \l__sdaps_questionnaire_iow
\bool_new:N \l__sdaps_questionnaire_parse_direct_bool

\bool_new:N \l__sdaps_questionnaire_info_bool

\keys_define:nn { sdaps / questionnaire }
{
  info         .bool_set:N  = \l__sdaps_questionnaire_info_bool,
  info         .default:n   = true,
  info         .initial:n   = true,

  noinfo       .meta:n  = { info=false },
}

\cs_new_protected_nopar:Nn \_sdaps_classic_show_info:
{
  \bool_if:NT \l__sdaps_questionnaire_info_bool {
    \begin{info}
      \translate{infotext} \\[1ex]
      \tl_if_eq:VnTF \g_sdaps_checkmode_tl { checkcorrect } {
        \begin{tabularx}{\textwidth}{lXllll}
          \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} & \hspace{1em} \correctedbox {} & \translate{info-corrected} \\
          \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} & \hspace{1em} \correctedbox* {} & \translate{info-corrected} \\
        \end{tabularx}
      } {
        \tl_if_eq:VnTF \g_sdaps_checkmode_tl { check } {
          \begin{tabularx}{\textwidth}{lXll}
            \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} \\
            \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} \\
          \end{tabularx}
        } {
          \tl_if_eq:VnTF \g_sdaps_checkmode_tl { fill } {
            \begin{tabularx}{\textwidth}{lXll}
              \checkbox & \strut \translate{info-multi} & \hspace{2em} \filledbox {} & \translate{info-fill} \\
              \checkbox* & \strut \translate{info-single} & \hspace{2em} \filledbox* {} & \translate{info-fill} \\
            \end{tabularx}
          } {
            \PackageError{sdaps}{Sorry, there is no help text for the checkmode you have choosen right now! Please pass the noinfo optional argument to the questionnaire environment!}\@ehb %
          }
        }
      }
    \end{info}
  }
  \ignorespaces
}

\newenvironment { questionnaire } [ 1 ] []
{
  \keys_set:nn { sdaps / questionnaire } { #1 }

  \hypersetup{
    pdfauthor     = \@author,
    pdftitle      = \@title,
    pdfsubject    = sdaps questionnaire \@title,
    pdfkeywords   = sdaps questionnaire \@title
  }

  % If we only have one questionnaire ID, then parse the environment directly,
  % otherwise write it into a temporary file and input it multiple times.
  \group_begin:

  \if@twoside
    \bool_gset_true:N \g_sdaps_twoside_bool
  \else
    \bool_gset_false:N \g_sdaps_twoside_bool
  \fi

  % Enable all metadata writing by default
  \sdaps_context_enable_writing:

  \sdaps_info_write:x{Author=\exp_not:o{\@author}}
  \sdaps_info_write:x{Title=\exp_not:o{\@title}}

  \int_compare:nTF { \seq_count:N \g_sdaps_questionnaire_ids_seq <= 1 } {
    \bool_set_true:N \l__sdaps_questionnaire_parse_direct_bool

    % Set the questionnaire ID
    \seq_gpop_left:NN \g_sdaps_questionnaire_ids_seq \l_tmpa_tl
    \sdaps_set_questionnaire_id:V \l_tmpa_tl

    % And, begin the questionnaire
    \sdaps_begin:
      \_sdaps_classic_show_info:
  } {
    \bool_set_false:N \l__sdaps_questionnaire_parse_direct_bool

    % Write content into a file, see "verbatim" documentation for more information.
    % TODO: Allow multiple temporary files by postfixing with integer?
    \iow_open:Nn \l__sdaps_questionnaire_iow { \c_sys_jobname_str . questionnaire }
    \cs_set_eq:NN\do\@makeother\dospecials
    \catcode`\^^M\active
    \def\verbatim@processline{
      \iow_now:Nx \l__sdaps_questionnaire_iow {\the\verbatim@line}
    }
    \verbatim@
  }
} {
  \bool_if:NTF \l__sdaps_questionnaire_parse_direct_bool {
        % Just end everything, nothing else to do.
        % Make sure we always end the current paragraph.
        \par
      \sdaps_end:

    \group_end:
  } {
       % We are done inputting the questionnaire
       \iow_close:N \l__sdaps_questionnaire_iow
    \group_end:

    % Now cycle through all IDS and output it again.
    \group_begin:
      \seq_map_inline:Nn \g_sdaps_questionnaire_ids_seq {
        \sdaps_set_questionnaire_id:n { ##1 }

        % Reset a lot of global LaTeX counters
        \@ifundefined{c@page}{}{\setcounter{page}{1}}
        \@ifundefined{c@part}{}{\setcounter{part}{0}}
        \@ifundefined{c@chapter}{}{\setcounter{chapter}{0}}
        \@ifundefined{c@paragraph}{}{\setcounter{paragraph}{0}}
        \@ifundefined{c@subparagraph}{}{\setcounter{subparagraph}{0}}
        \@ifundefined{c@section}{}{\setcounter{section}{0}}
        \@ifundefined{c@subsection}{}{\setcounter{subsection}{0}}
        \@ifundefined{c@subsubsection}{}{\setcounter{subsubsection}{0}}
        \@ifundefined{c@equation}{}{\setcounter{equation}{0}}
        \@ifundefined{c@figure}{}{\setcounter{figure}{0}}
        \@ifundefined{c@table}{}{\setcounter{table}{0}}

        \sdaps_begin:
          \_sdaps_classic_show_info:
          \input{ \c_sys_jobname_str . questionnaire }
          % Make sure we always end the current paragraph.
          \par
        \sdaps_end:
        \newpage

        % Close the output file now (after the page has been shipped out)
        % XXX: This is a bit of a hack, as disabling metadata writing has side effects
        \iow_close:N \g_sdaps_infofile_iow

      }
    \group_end:
  }
}

\def\sdapspagemark{
  \sdaps_page_end:
}

%    \end{macrocode}
%
% \subsection{The group environments}
%
% Alias for the group environments.
%
%    \begin{macrocode}
%

\tl_new:N \l__sdaps_classic_group_var_tl
\tl_new:N \l__sdaps_classic_group_text_tl
\tl_new:N \l__sdaps_classic_group_extra_tl
\keys_define:nn { sdapsclassic / group }
{
  var        .tl_set:N   = \l__sdaps_classic_group_var_tl,
  text       .tl_set:N   = \l__sdaps_classic_group_text_tl,
}

\newenvironment { choicegroup } [ 2 ] []
{
  \group_begin:
    \keys_set_known:nnN { sdapsclassic / group } { #1 } \l__sdaps_classic_group_extra_tl

    \_sdaps_classic_question{#2}
    \tl_if_empty:NTF \l__sdaps_classic_group_text_tl {
      \sdaps_qobject_begin:nnn { choicegroup } { Head } { #2 }
    } {
      \sdaps_qobject_begin:nnV { choicegroup } { Head } \l__sdaps_classic_group_text_tl
    }

    \tl_if_empty:NF \l__sdaps_classic_group_var_tl {
      \sdaps_qobject_append_var:V \l__sdaps_classic_group_var_tl
    }

    % Undefine the question (and choice) commands in local scope so that they
    % can be redefined by choicearray without any issues.
    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
    \cs_set_eq:NN\question\tex_undefined:D
    \cs_set_eq:NN\choice\tex_undefined:D

    \expandafter\choicearray\expandafter[\l__sdaps_classic_group_extra_tl]
      % XXX: This is a bit of a hack, set in global scope because the choicearray
      %      environment does evil things with scopes.
      \cs_gset_eq:NN\groupaddchoice\choice
      \cs_gset_eq:NN\choiceline\question
}
{
      \cs_gset_eq:NN\groupaddchoice\undefined
      \cs_gset_eq:NN\choiceline\undefined

    \endchoicearray

    \sdaps_qobject_end:n { choicegroup }
  \group_end:
}

\newenvironment { optiongroup } [ 2 ] []
{
  \group_begin:
    \keys_set_known:nnN { sdapsclassic / group } { #1 } \l__sdaps_classic_group_extra_tl

    \_sdaps_classic_question{#2}
    \tl_if_empty:NTF \l__sdaps_classic_group_text_tl {
      \sdaps_qobject_begin:nnn { optiongroup } { Head } { #2 }
    } {
      \sdaps_qobject_begin:nnV { optiongroup } { Head } \l__sdaps_classic_group_text_tl
    }

    \tl_if_empty:NF \l__sdaps_classic_group_var_tl {
      \sdaps_qobject_append_var:V \l__sdaps_classic_group_var_tl
    }

    % Undefine the question (and choice) commands in local scope so that they
    % can be redefined by optionarray without any issues.
    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
    \cs_set_eq:NN\question\tex_undefined:D
    \cs_set_eq:NN\choice\tex_undefined:D

    \expandafter\optionarray\expandafter[\l__sdaps_classic_group_extra_tl]
      % XXX: This is a bit of a hack, set in global scope because the optionarray
      %      environment does evil things with scopes.
      \cs_gset_eq:NN\groupaddchoice\choice
      \cs_gset_eq:NN\optionline\question
}
{
      \cs_gset_eq:NN\groupaddoption\undefined
      \cs_gset_eq:NN\optionline\undefined

    \endoptionarray
    \sdaps_qobject_end:n { optiongroup }
  \group_end:
}

\newenvironment { markgroup } [ 2 ] []
{
  \group_begin:
    \keys_set_known:nnN { sdapsclassic / group } { #1 } \l__sdaps_classic_group_extra_tl

    \_sdaps_classic_question{#2}
    \tl_if_empty:NTF \l__sdaps_classic_group_text_tl {
      \sdaps_qobject_begin:nnn { markgroup } { Head } { #2 }
    } {
      \sdaps_qobject_begin:nnV { markgroup } { Head } \l__sdaps_classic_group_text_tl
    }

    \tl_if_empty:NF \l__sdaps_classic_group_var_tl {
      \sdaps_qobject_append_var:V \l__sdaps_classic_group_var_tl
    }

    % Undefine the range command in local scope so that it can be redefined
    % by rangearray without any issues.
    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
    \cs_set_eq:NN\range\tex_undefined:D

    \expandafter\rangearray\expandafter[\l__sdaps_classic_group_extra_tl]
      % XXX: This is a bit of a hack, set in global scope because the choicearray
      %      environment does evil things with scopes.
      \cs_gset_eq:NN\markline\range
}
{
      \cs_gset_eq:NN\markline\undefined

    \endrangearray
    \sdaps_qobject_end:n { markgroup }
  \group_end:
}

\ExplSyntaxOff

%    \end{macrocode}

% \Finale
\endinput
