% \iffalse meta-comment
%
% Copyright (C) 2022 by Tanguy Ortolo <tanguy+latex@ortolo.eu>
% 
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
% 1. Redistributions of source code must retain the above copyright notice,
%    this list of conditions and the following disclaimer.
% 2. Redistributions in binary form must reproduce the above copyright notice,
%    this list of conditions and the following disclaimer in the documentation
%    and/or other materials provided with the distribution.
% 3. Neither the name of the copyright holder nor the names of its
%    contributors may be used to endorse or promote products derived from this
%    software without specific prior written permission.
% 
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
%
% \fi
%
% \iffalse
%<*driver>
\ProvidesFile{songproj.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[2020/10/01]
%<package>\ProvidesPackage{songproj}
%<*package>
    [2022/11/07 v1.0.1 Song projection]
%</package>
%
%<*driver>
\documentclass{l3doc}
\usepackage{fontspec}
\usepackage{bookmark}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{songproj.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{256}
%
% \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.0}{2021/09/19}{Initial version}
% \changes{v0.3.0}{2021/09/26}{Use \textsf{expl3} naming conventions}
% \changes{v1.0.0}{2021/11/07}{First public release}
% \changes{v1.0.1}{2021/11/07}{Add the generated extension file to Git}
%
% \GetFileInfo{songproj.dtx}
%
% \DoNotIndex{\newcommand,\newenvironment}
% \DoNotIndex{\begin,\end}
% \DoNotIndex{\bool,\bool_gset_false:N,\bool_gset_true:N,\bool_if:NTF,\bool_new:N}
% \DoNotIndex{\cs_gset:Npn,\cs_new:Npn}
% \DoNotIndex{\dim,\dim_compare:nNnTF,\dim_new:N,\dim_zero:N}
% \DoNotIndex{\int,\int_compare:nNnTF,\int_gset_eq:NN,\int_new:N,\int_step_function:nN,\int_eval:n,\int_mod:nn,\int_step_inline:nn,\int_use:N}
% \DoNotIndex{\seq,\seq_count:N,\seq_gclear:N,\seq_gput_right:Nn,\seq_gset_from_clist:Nn,\seq_if_empty:NTF,\seq_item:Nn,\seq_map_function:NN,\seq_new:N}
% \DoNotIndex{\tl,\tl_gclear:N,\tl_gset:Nn,\tl_new:N,\tl_use:N,\tl_if_empty:NTF}
% \DoNotIndex{\IfNoValueTF,\NewDocumentCommand,\NewDocumentEnvironment}
% \DoNotIndex{\env,\cmd,\textsf,\vfill}
%
%
% \title{The \textsf{songproj} package\thanks{This document
%   corresponds to \textsf{songproj}~\fileversion, dated \filedate.}}
% \author{Tanguy Ortolo \\ \texttt{tanguy+latex@ortolo.eu}}
%
% \maketitle
% \section{Introduction}
%
% This package, together with the \cls{beamer} class, is used to generate
% slideshows with song lyrics. This is typically used in religious services in
% churches equipped with a projector, for which this package has been written,
% but it can be useful for any type of singing assembly\footnote{Indeed, the
% song used here as an example is not really a religious one! It was chosen
% because it is in the public domain and the author likes it.}. It provides
% environments to describe a song in a natural way, and formatting it into
% slides with overlays.
%
% \section{Usage}
%
% \subsection{The \env{song} environment}
%
% The main feature of this package is the \env{song}, that allows the user to
% describe an entire song that will be formatted into slides.
%
% \paragraph{}
% \DescribeEnv{song}
% The \env{song}\marg{stanzas per slide}\oarg{couplet list} environment is used
% around an entire song. It takes a mandatory argument, \meta{stanzas per
% slide}, to specify whether the user wants to show one or two
% stanzas\footnote{including the refrain} on the slide. An optional argument,
% \meta{couplet list} is a comma-separated list of couplet (or verse) indexes,
% that allows the user to indicate that they want to include only these
% couplets of a large song: without this, all couplets will be included.
%
% Inside of the \env{song} environment, the user will use the \cs{longest}
% command and the \env{intro}, \env{refrain}, \env{couplet}\footnote{We
% chose to use the French words \emph{refrain} and \emph{couplet} for several
% reason: the author is French, these words are understandable in English and
% their English equivalents, \emph{chorus} and \emph{verse}, have multiple
% meanings that would make them very ambiguous in both usage and implementation
% of this package.} and \env{final} environments.
%
% \paragraph{Warning} Inside a \env{song} environment, it is an error to write
% anything that is not an \env{intro}, \env{refrain}, \env{couplet},
% \env{final} environment or a \cs{longest} command. Direct text would be
% typeset in a way that would disrupt the song formatting.
%
% \paragraph{}
% \begin{function}{\longest}
% \begin{syntax}
% \cs{longest}\marg{song line}
% \end{syntax}
% Inside a \env{song} environment, the \cs{longest}\marg{song line} command is
% used to declare the longest line of a song, that will be used to properly
% center the song stanzas, as allowed by the \pkg{verse} package. That line
% is only used to compute and record its length, and will not be typeset.
% \end{function}
%
% \changes{v0.4.0}{2021/09/29}{Document \env{intro} and \env{final} environments}
%
% \paragraph{}
% \DescribeEnv{intro}
% Inside a \env{song} environment, the optional \env{intro} environment
% declares a number of lines meant to be sung once, at the beginning of the
% song. In a psalm, this may be an antiphon.
%
% \paragraph{}
% \DescribeEnv{refrain}
% Inside a \env{song} environment, the optional \env{refrain} environment
% declares the song refrain (or chorus). A song may start with its refrain, or
% with a first couplet, followed by the refrain. It is not useful to declare
% the refrain several time, as the \env{song} environment will take care of
% repeating between the couplets.
%
% \paragraph{}
% \DescribeEnv{couplet}
% Inside a \env{song} environment, the \env{couplet} environment declares each
% couplet (or verse) of the song.
%
% \paragraph{}
% \DescribeEnv{final}
% Inside a \env{song} environment, the optional \env{final} environment
% declares a number of lines meant to be sung once, at the end of the song. In
% an hymn, that may be a doxology.
%
% \paragraph{Example} \label{example/song}
% The following song is defined with three couplets and a refrain. Since its
% begins with a couplet, it will be formatted with the first couplet, the
% refrain, the second couplet, the refrain, and so on.
%
% The \env{song} environment is given two arguments, |{2}[1,2]|. The first one
% tells it to show two stanzas, that is, both a couplet and the refrain, on the
% generated slide. The second argument tells it to include only the first two
% couplets in the output.
%
% \begin{verbatim}
% \begin{frame}
%   \begin{song}{2}[1,2]
%     \longest{Light she was, and like a fairy,}
%     \begin{couplet}
%       In a cavern, in a canyon, \\
%       Excavating for a mine. \\
%       Dwelt a miner, forty-niner, \\
%       And his daughter, Clementine. \\
%     \end{couplet}
%     \begin{refrain}
%       Oh my darling, oh my darling, \\
%       Oh my darling Clementine, \\
%       You are lost and gone forever, \\
%       Dreadful sorry, Clementine. \\
%     \end{refrain}
%     \begin{couplet}
%       Light she was, and like a fairy, \\
%       And her shoes were number nine, \\
%       Herring boxes, without topses, \\
%       Sandals were for Clementine. \\
%     \end{couplet}
%     \begin{couplet}
%       […]
%     \end{couplet}
%   \end{song}
% \end{frame}
% \end{verbatim}
%
% \subsection{The \cs{inputsong} command}
% \changes{v1.0.0}{2021/11/07}{Document the \cs{inputsong} command}
%
% \begin{function}{\inputsong}
% \begin{syntax}
% \cs{inputsong}\marg{file}\marg{stanzas per slide}\oarg{couplet list}
% \end{syntax}
%
% The \cs{inputsong} command environment is used as a shortcut for
% typesetting a song written in an external file. That file should contain the
% song content, including intro, refrain, couplet or final as needed, but
% \emph{without} being wrapped in a \env{song} environment.
%
% For instance, one could write a file named \file{clementine.tex} containing
% the \emph{content} of the \env{song} environment shown in example
% page~\pageref{example/song}, and use it in a slideshow:
%
% \begin{verbatim}
% \frame{\inputsong{clementine.tex}{2}[1,2]}
% \end{verbatim}
% \end{function}
%
% \subsection{The \env{refrain}, \env{couplet}, \env{intro} and \env{final} environments}
%
% \changes{v0.4.0}{2021/09/29}{Document \env{intro} and \env{final} environments}
%
% These commands are also usable outside of a \env{song} environment. In that
% case, they simply format a refrain or couplet, which can be useful when you
% need more manual control.
%
% \paragraph{}
% \DescribeEnv{refrain}
% Outside of a \env{song} environment, this environment simply wraps its
% content inside a \env{structure} and a \env{verse} environment. It takes an
% optional \meta{verse width} argument, that is used to properly center the
% refrain, as allowed by the \pkg{verse} package.
%
% \paragraph{}
% \DescribeEnv{couplet}
% Outside of a \env{song} environment, this environment simply wraps its
% content inside a \env{verse} environment. It takes an optional \meta{verse
% width} argument, that is used to properly center the refrain, as allowed by
% the \pkg{verse} package.
%
% \paragraph{}
% \DescribeEnv{intro}
% \DescribeEnv{final}
% Outside of a \env{song} environment, these environments simply wrap their
% content inside a \env{em} and a \env{verse} environment. They takes an
% optional \meta{verse width} argument, that is used to properly center the
% refrain, as allowed by the \pkg{verse} package.
%
% \subsection{Usage tips}
%
% For regular offices, there are several suggestions that can ease the creation
% and usage of lyric slideshows.
%
% \subsubsection{Using dedicated song files}
%
% It is suggested to write song lyrics in dedicated files, each containing a
% single the \emph{content} of a \env{song} environment, without the
% environment wrapping itself. They can then be used with the \cs{inputsong}
% command.
%
% For instance, one could write a file named \file{clementine.tex} containing
% the \emph{content} of the \env{song} environment shown in example
% page~\pageref{example/song}. It would then be used in a slideshow such as:
%
% \begin{verbatim}
% \documentclass{beamer}
% \usepackage{songproj}
%
% \begin{document}
%   \begin{frame}
%     \inputsong{clementine.tex}{2}[1,2,3]
%   \end{frame}
% \end{document}
% \end{verbatim}
%
% \subsubsection{Importing text lyrics}
%
% Song lyrics are often found in text format with basic markup:
%
% \begin{verbatim}
% 1. In a cavern, in a canon,
% Excavating for a mine.
% Dwelt a miner, forty-niner,
% And his daughter, Clementine.
%
% C. Oh my darling, oh my darling,
% Oh my darling Clementine
% You are lost and gone forever,
% Dreadful sorry Clementine.
%
% 2. Light she was, and like a fairy,
% And her shoes were number nine,
% Herring boxes, without topses,
% Sandals were for Clementine.
%
% […]
% \end{verbatim}
%
% To avoid the tedious task of manually removing text and adding \LaTeX{}
% markup, we provide the \file{song2tex.py} helper. Please refer to its
% embedded help for detailed instructions about its usage:
%
% \begin{verbatim}
% $ ./song2tex.py --help
% $ ./song2tex.py clementine.txt clementine.tex
% \end{verbatim}
%
% \subsubsection{Projection layout advice}
% \changes{v1.0.0}{2021/11/07}{Add projection layout advice}
%
% During a religious service, a song lyrics projection is only a support, and
% should not draw their attention away from the main feature, which is the
% common prayer.
%
% I therefore suggest using a very simple Beamer theme, such as its default one
% with the \href{https://github.com/rchurchley/beamercolortheme-owl}{owl} color
% theme, and removing the navigation symbols. I also suggest not showing song
% titles (or anything else that is not actually sung by the assembly) unless
% there is a good reason to do so, such as getting used to a song or set of
% songs you intend to reuse often.
%
% \begin{verbatim}
% \documentclass{beamer}
% \usecolortheme{owl}
% \setbeamertemplate{navigation symbols}{}
% \usepackage{songproj}
% \begin{document}
%   […]
% \end{document}
% \end{verbatim}
%
% \subsubsection{Projection advice}
% \changes{v1.0.0}{2021/11/07}{Add projection advice}
%
% For projecting song lyrics, you can take advantage of using a PDF
% presentation software able to show a presenter console on your laptop screen,
% and the current slide on the projector. Software like as
% \href{https://pdfpc.github.io/}{pdfpc} or
% \href{https://github.com/Cimbali/pympress/}{Pympress} can also understand and
% adapt their display to the concept of Beamer overlay.
%
% \StopEventually{}
%
% \section{Implementation}
%
% \subsection{Dependencies}
%
% This module is written using \LaTeX3 programming interfaces and command
% definitions:
%
%    \begin{macrocode}
\RequirePackage{expl3}
\RequirePackage{xparse}
%    \end{macrocode}
%
% The implementation of the \env{song} environment and its friends is mainly
% based on the \textsf{verse} package:
%
%    \begin{macrocode}
\RequirePackage{verse}
%    \end{macrocode}
%
% \subsection{Internal definitions}
%
% Almost all definitions use the \textsf{expl3} syntax:
%
%    \begin{macrocode}
\ExplSyntaxOn
%    \end{macrocode}
%
% \subsubsection{Internal variables}
%
% \changes{v0.2.0}{2021/09/23}{Use \texttt{sp} prefix for functions and
%   variables}
%
% We define a number of internal variables, that are used when reading and
% formatting a song. All of these variables are meant to be set globally: since
% there is no notion of a song within a song, we are certain that we will
% always be either outside of a song or inside a single song.
%
%    \begin{macrocode}
\bool_new:N \g__sp_song_bool             % are we in a song?
\bool_new:N \g__sp_song_start_bool       % are we at the start of a song?
\bool_new:N \g__sp_refrain_first_bool    % does current song start with the
                                         % refrain?
\int_new:N  \g__sp_stanzas_per_slide_int % number of stanzas to show on each
                                         % slide (1 or 2)
\dim_new:N  \g__sp_linewidth_dim         % length of the longest line in current
                                         % song
\tl_new:N   \g__sp_intro_tl              % current song intro
\tl_new:N   \g__sp_refrain_tl            % current song refrain
\seq_new:N  \g__sp_couplets_seq          % current song couplets
\tl_new:N   \g__sp_final_tl              % current song final
\seq_new:N  \g__sp_couplet_indexes_seq   % indexes of couplets to include
%    \end{macrocode}
%
% \subsubsection{Internal environments}
%
% These are high-level internal environments, that are used in the
% implementation of user interface environments.
%
% \begin{environment}{__sp_refrain}
% This environment simply formats a song refrain. It is used in the user interface
% \env{refrain} environment.
%
%    \begin{macrocode}
\NewDocumentEnvironment {__sp_refrain} {}
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the verse environment and
  % constitute its optional argument.
  {
    \begin{structureenv}
    \begin{verse}
  }
  {
    \end{verse}
    \end{structureenv}
  }
%    \end{macrocode}
% \end{environment}
%
% \begin{environment}{__sp_couplet}
% This environment simply formats a song couplet. It is used in the user interface
% \env{couplet} environment.
%
%    \begin{macrocode}
\NewDocumentEnvironment {__sp_couplet} {}
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the verse environment and
  % constitute its optional argument.
  { \begin{verse} }
  { \end{verse} }
%    \end{macrocode}
% \end{environment}
%
% \begin{environment}{__sp_special} {}
%
% \changes{v0.4.0}{2021/09/29}{Add an environment to format intro and final}
%
% This environments simply formats a song intro of final. It is used in the
% user interface \env{intro} and \env{final} environments.
%
%    \begin{macrocode}
\NewDocumentEnvironment {__sp_special} {}
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the verse environment and
  % constitute its optional argument.
  {
    \begin{em}
    \begin{verse}
  }
  {
    \end{verse}
    \end{em}
  }
%    \end{macrocode}
% \end{environment}
%
% \subsubsection{Internal macros}
% \changes{v0.3.0}{2021/09/26}{Define aux functions at top-level}
%
% These are macros that are used in the implementation of the \env{song}
% environment.
%
% \begin{macro}{\__sp_song_refrain}
%
% This macro uses the \env{__sp_refrain} environment to format the current song
% refrain.
%
%    \begin{macrocode}
\tl_gset:Nn \__sp_song_refrain
  {
    % Do we know the width of the longest song line?
    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
      { \begin{__sp_refrain} }
      { \begin{__sp_refrain} [\g__sp_linewidth_dim] }
    \tl_use:N \g__sp_refrain_tl
    \end{__sp_refrain}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\__sp_song_couplet:n}
%
% This macro uses the \env{__sp_couplet} environment to a specified couplet of
% the current song. It takes a single argument:
%
% \begin{arguments}
%   \item index of the couplet to format.
% \end{arguments}
%
%    \begin{macrocode}
\cs_gset:Npn \__sp_song_couplet:n #1
  {
    % Do we know the width of the longest song line?
    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
      { \begin{__sp_couplet} }
      { \begin{__sp_couplet} [\g__sp_linewidth_dim] }
    \seq_item:Nn \g__sp_couplets_seq {#1}
    \end{__sp_couplet}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\__sp_song_couplets:n}
%
% This macro inserts an containing all couplets of the current song in an
% \env{overprint} environment, in groups separated with \cs{onslide}
% commands. It takes a single argument:
%
% \begin{arguments}
%   \item number of couplets to show together on each slide.
% \end{arguments}
%
%    \begin{macrocode}
\cs_gset:Npn \__sp_song_couplets:n #1
  {
    \begin{overprint}
    % Loop on all specified couplets
    \int_step_inline:nn
      { \seq_count:N \g__sp_couplet_indexes_seq }
      {
        % Before every #1 lines, i.e. when (##1 - 1) mod #1 == 0),
        % insert an \onslide
        \int_compare:nNnTF
          { \int_mod:nn { \int_eval:n{##1 - 1} } {#1} } { = } { 0 }
          { \onslide<+> }
          { \vskip \stanzaskip }
        \__sp_song_couplet:n { \seq_item:Nn \g__sp_couplet_indexes_seq {##1} }
      }
    \end{overprint}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\__sp_song_intro}
%
% \changes{v0.4.0}{2021/09/29}{Add an environment to format current song intro}
%
% This macro uses the \env{__sp_special} environment to format the current song
% intro.
%
%    \begin{macrocode}
\tl_gset:Nn \__sp_song_intro
  {
    % Do we know the width of the longest song line?
    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
      { \begin{__sp_special} }
      { \begin{__sp_special} [\g__sp_linewidth_dim] }
    \tl_use:N \g__sp_intro_tl
    \end{__sp_special}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\__sp_song_final}
%
% \changes{v0.4.0}{2021/09/29}{Add an environment to format current song final}
%
% This macro uses the \env{__sp_refrain} environment to format the current song
% final.
%
%    \begin{macrocode}
\tl_gset:Nn \__sp_song_final
  {
    % Do we know the width of the longest song line?
    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
      { \begin{__sp_special} }
      { \begin{__sp_special} [\g__sp_linewidth_dim] }
    \tl_use:N \g__sp_final_tl
    \end{__sp_special}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\__sp_song}
%
% \changes{v0.4.0}{2021/09/29}{Include intro and final in complete song}
%
% This macro inserts the entire song, alternating refrain and couplets in an
% \env{overprint} environment.
%
%    \begin{macrocode}
\tl_gset:Nn \__sp_song
  {
    % Is there a song intro?
    \tl_if_empty:NTF \g__sp_intro_tl
    {}
    {
      \visible<1> {\__sp_song_intro}
      % The combination of overprint with verse that comes next somehow adds
      % extra vertical space that needs to be removed.
      \vskip -\stanzaskip
    }

    \begin{overprint}

    % Does the song begin with the refrain?
    \bool_if:NTF \g__sp_refrain_first_bool
      {
        % If so, print an initial refrain
        \onslide<+>
        \__sp_song_refrain
      }
      {}

    % Is there a refrain?
    \tl_if_empty:NTF \g__sp_refrain_tl
      {
        % No refrain, loop on all specified couplets and insert them
        \seq_map_inline:Nn
          \g__sp_couplet_indexes_seq
          {
            \onslide<+>
            \__sp_song_couplet:n {#1}
          }
      }
      {
        % There is a refrain, loop on all specified couplets and, each time,
        % insert both a couplet and the refrain
        \seq_map_inline:Nn
          \g__sp_couplet_indexes_seq
          {
            \onslide<+>
            \__sp_song_couplet:n {#1}
            \onslide<+>
            \__sp_song_refrain
          }
      }
    \end{overprint}

    % Is there a song final?
    \tl_if_empty:NTF \g__sp_final_tl
    {}
    {
      % Add extra spacing
      \vskip \stanzaskip
      \visible<.> {\__sp_song_final}
    }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{User interface}
%
% These environments constitute our user interface. They allow the user to
% define songs, refrains and couplets.
%
% \begin{environment}{refrain}
% This environment handles a refrain :
% \begin{itemize}
%   \item outside of a song, it uses the \env{__sp_refrain} environment to
%     directly format it ;
%   \item inside a song, it stores it into \cs{g__sp_retrain_tl}, so it can
%     be formatted by the end of the \env{song} environment.
% \end{itemize}
%
%    \begin{macrocode}
\NewDocumentEnvironment {refrain} { +b }
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the __sp_refrain
  % environment and constitute its optional argument.
  {
    % Are we in a song?
    \bool_if:NTF \g__sp_song_bool
      {
        % We are in a song, are we at its start?
        \bool_if:NTF \g__sp_song_start_bool
          {
            % Indicate that we are no longer at the start of the song
            \bool_gset_false:N\g__sp_song_start_bool
            % and that the refrain comes first
            \bool_gset_true:N\g__sp_refrain_first_bool
          }
          {}
        % Anyway, store the refrain
        \tl_gset:Nn \g__sp_refrain_tl {#1}
      }
      {
        % We are not in a song, use __sp_refrain to format the refrain
          \begin{__sp_refrain}
            #1
          \end{__sp_refrain}
      }
  }
  {}
%    \end{macrocode}
% \end{environment}
%
% \begin{environment}{couplet}
% This environment handles a couplet, in a similar way:
% \begin{itemize}
%   \item outside of a song, it uses the \env{__sp_couplet} environment to
%     directly format it ;
%   \item inside a song, it stores it by appending it to to
%     \cs{g__sp_couplets_seq}, so it can be formatted by the end of the
%     \env{song} environment.
% \end{itemize}
%
%    \begin{macrocode}
\NewDocumentEnvironment {couplet} { +b }
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the __sp_couplet
  % environment and constitute its optional argument.
  {
    % Are we in a song?
    \bool_if:NTF \g__sp_song_bool
      {
        % Are we at in a song, are we at its start?
        \bool_if:NTF \g__sp_song_start_bool
          {
            % Indicate that we are no longer at the start of the song
            \bool_gset_false:N \g__sp_song_start_bool
            % and that the refrain does not come first
            \bool_gset_false:N \g__sp_refrain_first_bool
          }
          {}
        % Anyway, store this couplet
        \seq_gput_right:Nn \g__sp_couplets_seq { {#1} }
      }
      {
        % We are not in a song, use __sp_couplet to format this couplet
          \begin{__sp_couplet}
            #1
          \end{__sp_couplet}
      }
  }
  {}
%    \end{macrocode}
% \end{environment}
%
% \begin{environment}{intro}
%
% \changes{v0.4.0}{2021/09/29}{Add an \env{intro} environment}
%
% This environment handles a song intro, in a similar way:
% \begin{itemize}
%   \item outside of a song, it uses the \env{__sp_special} environment to
%     directly format it;
%   \item inside a song, it stores it  into \cs{g__sp_intro_tl} so it can be
%     formatted by the end of the \env{song} environment.
% \end{itemize}
%
%    \begin{macrocode}
\NewDocumentEnvironment {intro} { +b }
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the __sp_special
  % environment and constitute its optional argument.
  {
    % Are we in a song?
    \bool_if:NTF \g__sp_song_bool
      {
        % We are in a song, store its intro
        \tl_gset:Nn \g__sp_intro_tl {#1}
      }
      {
        % We are not in a song, use __sp_special to format the intro
        \begin{__sp_special}
          #1
        \end{__sp_special}
      }
  }
  {}
%    \end{macrocode}
% \end{environment}
%
% \begin{environment}{final}
%
% \changes{v0.4.0}{2021/09/29}{Add a \env{final} environment}
%
% This environment handles a song final, in a similar way:
% \begin{itemize}
%   \item outside of a song, it uses the \env{__sp_special} environment to
%     directly format it;
%   \item inside a song, it stores it  into \cs{g__sp_final_tl} so it can be
%     formatted by the end of the \env{song} environment.
% \end{itemize}
%
%    \begin{macrocode}
\NewDocumentEnvironment {final} { +b }
  % The environment opening may be followed by a [length], in fact part of its
  % body, and will appear just after the opening of the __sp_special
  % environment and constitute its optional argument.
  {
    % Are we in a song?
    \bool_if:NTF \g__sp_song_bool
      {
        % We are in a song, store its intro
        \tl_gset:Nn \g__sp_final_tl {#1}
      }
      {
        % We are not in a song, use __sp_special to format the intro
        \begin{__sp_special}
          #1
        \end{__sp_special}
      }
  }
  {}
%    \end{macrocode}
% \end{environment}
%
% \begin{macro}{\longest}
% This macro measures the length of a song line and stores it, so it can be
% used by the \env{song} environment to properly center refrain and couplets.
% It takes a single argument:
%
% \begin{arguments}
%   \item a song line to measure.
% \end{arguments}
%
%    \begin{macrocode}
\NewDocumentCommand {\longest} { m } { \settowidth {\g__sp_linewidth_dim} {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{environment}{song}
%
% This environment is used as a container for entire songs. On opening, it does several things:
%   \begin{enumerate}
%     \item its stores its arguments into variables with a descriptive name;
%     \item it clears out any previously stored refrain, couplet, intro, final
%       and longest song line;
%     \item it sets the \cs{g__sp_song_bool} variable to indicate that we
%       are inside a song, which will alter the behaviour of the
%       \env{refrain} and \env{couplet} environments so they record their
%       content rather than directly formatting it into the document;
%     \item it sets the \cs{g__sp_song_start_bool} variable to indicate that
%       we are at the start of the song, which will allow the next
%       \env{refrain} or \env{couplet} to tell if the song starts with the
%       refrain or with a couplet;
%   \end{enumerate}
%
% This environment takes two arguments:
%
% \begin{arguments}
%   \item number of stanzas (counting couplets and refrain, when there is one) per slide;
%   \item list of couplets to include (defaults to all), for instance |1,3,4|.
% \end{arguments}
%
%    \begin{macrocode}
\NewDocumentEnvironment {song} { m o }
  % {number of stanzas per slide (1 or 2)}
  % [list of couplets to include (defaults to all)]
  {
    % Put arguments into variables with understandable names
    \int_gset_eq:NN {\g__sp_stanzas_per_slide_int} {#1}
    \IfNoValueTF {#2}
      { \seq_gclear:N \g__sp_couplet_indexes_seq }
      { \seq_gset_from_clist:Nn \g__sp_couplet_indexes_seq {#2} }
  
    % Clear out intro, refrain, couplet, final and longest song line
    \tl_gclear:N \g__sp_intro_tl
    \tl_gclear:N \g__sp_refrain_tl
    \seq_gclear:N \g__sp_couplets_seq
    \tl_gclear:N \g__sp_final_tl
    \dim_zero:N {\g__sp_linewidth_dim}
  
    % Indicate that we are in a song, and at its start
    \bool_gset_true:N \g__sp_song_bool
    \bool_gset_true:N \g__sp_song_start_bool
  }
%    \end{macrocode}
%
% And on closing:
% \begin{itemize}
%   \item if no list of couplet indexes to use have been given, it generates one
%     covering all couplets in order;
%   \item it uses internal functions to insert the intro, refrain, couplets and
%     final into the document, in the right order according to the song
%     structure (refrain or couplet first) and to the formatting instructions
%     (one or two stanzas per slide).
% \end{itemize}
%
% \changes{v0.4.0}{2021/09/29}{Include intro and final in formatted song}
%
%    \begin{macrocode}
  {
    % Have we been given indexes of specific couplets to use?
    \seq_if_empty:NTF \g__sp_couplet_indexes_seq
      {
        % If not, generate it from the list of couplets
        \int_step_inline:nn
          { \seq_count:N \g__sp_couplets_seq }
          { \seq_gput_right:Nn \g__sp_couplet_indexes_seq {##1} }
      }
      {}

    % Now we actually start inserting things into the document.
    % How many stanzas per side did the user request?
    \int_compare:nNnTF \g__sp_stanzas_per_slide_int {>} {1}
      {
        % More than one stanza per slide
        %
        % Is there an intro?
        \tl_if_empty:NTF \g__sp_intro_tl
          {}
          {
            \visible<1> {\__sp_song_intro}
            % Adjust vertical spacing depending on whether the refrain or the
            % couplets follow.
            \bool_if:NTF\g__sp_refrain_first_bool
              {
                % Refrain comes next, add extra space
                \vskip \parsep
              }
              {
                % Couplets come next, the combination of their overprint and
                % verse environment somehow adds extra vertical space that
                % needs to be removed.
                \vskip -\stanzaskip
              }
          }

        % Is there a refrain?
        \tl_if_empty:NTF \g__sp_refrain_tl
          {
            % If there is no refrain, we use \__sp_song_couplets:n to write the
            % couplets, \g__sp_stanzas_per_slide_int at a time.
            \__sp_song_couplets:n { \int_use:N \g__sp_stanzas_per_slide_int }
          }
          {
            % If there is a refrain, we use \__sp_song_refrain to write the
            % refrain and \__sp_song_couplets:n to write overprint with all
            % couplets.

            % Does the song begin with the refrain?
            \bool_if:NTF\g__sp_refrain_first_bool
              {
                \__sp_song_refrain
                \vskip -\stanzaskip
                \__sp_song_couplets:n 1
              }
              {
                \__sp_song_couplets:n 1
                \vskip \stanzaskip
                \__sp_song_refrain
              }
          }

        % Is there a final?
        \tl_if_empty:NTF \g__sp_final_tl
          {}
          {
            % Adjust vertical spacing depending on whether we follow the
            % refrain or the couplets.
            \tl_if_empty:NTF \g__sp_refrain_tl
              {
                % No refrain, we follow the couplets, add extra space
                \vskip \stanzaskip
              }
              {
                % There was a refrain, did it come first?
                \bool_if:NTF \g__sp_refrain_first_bool
                {
                  % Refrain came first, we follow the couplets, add extra space
                  \vskip \stanzaskip
                }
                {
                  % Refrain came last, we follow it, add extra space
                  \vskip \parsep
                }
              }
            \visible<.> {\__sp_song_final}
          }
      }
      {
        % If the user requested one stanza per slide, we use \__sp_song to
        % write the entire song in a single overprint environment.
        \__sp_song
      }
    % Indicate that we are no longer in a song
    \bool_gset_false:N\g__sp_song_bool
  }
%    \end{macrocode}
% \end{environment}
%
% \begin{macro}{\inputsong}
% \changes{v1.0.0}{2021/11/07}{Add a \cs{inputsong} command}
%
% This macro starts a \env{song} environment and \cs{input}s the song content
% from an external file.
%
%    \begin{macrocode}
\NewDocumentCommand {\inputsong} { m m o }
  {
    \IfNoValueTF {#3}
      { \begin{song} {#2} }
      { \begin{song} {#2} [#3] }
    \input{#1}
    \end{song}
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Wrapping up}
%
% Now that we have defined everything we need, we can leave the \textsf{expl3}
% syntax and return to normal \TeX{} syntax:
%
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
%
% \Finale
\endinput
