%Lyluatex LaTeX style.
%
% Copyright (C) 2015-2023 jperon and others (see CONTRIBUTORS.md)
% License: MIT
% This file is part of lyluatex.

\NeedsTeXFormat{LaTeX2e}%
\ProvidesPackage{lyluatex}[2023/03/01 v1.1.3]  %%LYLUATEX_DATE LYLUATEX_VERSION

% Dependencies
\RequirePackage{graphicx}
\RequirePackage{minibox}
\RequirePackage{environ}
\RequirePackage{currfile}
\RequirePackage{pdfpages}
\IfFileExists{varwidth.sty}{\RequirePackage{varwidth}}{}
\RequirePackage{luaoptions}

\RequirePackage{metalogo}
\newcommand{\lyluatex}{\textit{ly}\LuaTeX}

\edef\ly@false{false}\def\ly@istwosided{\if@twoside\else\ly@false\fi}
\savecatcodetable 40

% Copied from ifnextok.sty.
% We use \providecommand instead of \newcommand and \def in order
% to avoid overriding ifnextok if it is already loaded.
\providecommand{\IfNextToken}[3]{%
  \let\nextok@match= #1%
  \def\nextok@if{#2}\def\nextok@else{#3}%
  \futurelet\@let@token\nextok@decide%
}
\providecommand\nextok@decide{%
  \ifx\@let@token\nextok@match
    \expandafter\nextok@if
  \else
    \expandafter\nextok@else
  \fi%
}

% Options
\catcode`-=11
\directlua{
  local _opt = lua_options
  lua_options.register('ly', {
    ['addversion'] = {'false', 'true', ''},
    ['autoindent'] = {'true', 'false', ''},
    ['cleantmp'] = {'false', 'true', ''},
    ['currfiledir'] = {},
    ['debug'] = {'false', 'true', ''},
    ['extra-bottom-margin'] = {'0', _opt.is_dim},
    ['extra-top-margin'] = {'0', _opt.is_dim},
    ['fix_badly_cropped_staffgroup_brackets'] = {'false', 'true', ''},
      ['nofix_badly_cropped_staffgroup_brackets'] = {'default', _opt.is_neg},
    ['force-compilation'] = {'false', 'true', ''},
    ['fragment'] = {'', 'false', 'true'},
        ['nofragment'] = {'default', _opt.is_neg},
    ['fullpagealign'] = {'crop', 'staffline'},
    ['fullpagestyle'] = {''},
    ['gutter'] = {'.4in', _opt.is_dim},
        ['exampleindent'] = {'gutter', _opt.is_alias},
        ['leftgutter'] = {'', _opt.is_dim}, ['rightgutter'] = {'', _opt.is_dim},
    ['hpadding'] = {'0.75ex', _opt.is_dim},
    ['include_after_body'] = {'false'},
    ['include_before_body'] = {'false'},
    ['include_footer'] = {'false'},
    ['include_header'] = {'false'},
    ['includepaths'] = {'./'},
    ['indent'] = {'', _opt.is_dim},
        ['noindent'] = {'default', _opt.is_neg},
    ['insert'] = {'', 'systems', 'fullpage', 'inline', 'bare-inline'},
    ['intertext'] = {''},
    ['label'] = {'false'}, ['labelprefix'] = {'ly_'},
    ['line-width'] = {[[\linewidth]], _opt.is_dim},
    ['ly-version'] = {'2.18.2'},
    ['max-protrusion'] = {[[\maxdimen]], _opt.is_dim},
        ['max-left-protrusion'] = {'', _opt.is_dim},
        ['max-right-protrusion'] = {'', _opt.is_dim},
    ['noclef'] = {'false', 'true', ''},
    ['nostaff'] = {'false', 'true', ''},
    ['nostaffsymbol'] = {'false', 'true', ''},
    ['notime'] = {'false', 'true', ''},
    ['notiming'] = {'false', 'true', ''},
    ['notimesig'] = {'false', 'true', ''},
    ['optimize-pdf'] = {'false', 'true', ''},
    ['paperwidth'] = {[[\paperwidth]], _opt.is_dim},
    ['paperheight'] = {[[\paperheight]], _opt.is_dim},
    ['papersize'] = {'false'},
    ['pass-fonts'] = {'false', 'true', ''},
        ['current-font'] = {}, ['current-font-as-main'] = {'false', 'true', ''},
        ['rmfamily'] = {}, ['sffamily'] = {}, ['ttfamily'] = {},
    ['print-page-number'] = {'false', 'true', ''},
        ['first-page-number'] = {'false', ''},
        ['print-first-page-number'] = {'true', 'false', ''},
    ['print-only'] = {''},
        ['do-not-print'] = {''},
    ['printfilename'] = {'false', 'true', ''},
    ['program'] = {'lilypond'},
    ['protrusion'] = {'', _opt.is_dim},
        ['noprotrusion'] = {'default', _opt.is_neg},
    ['raw-pdf'] = {'false', 'true', ''},
    ['quote'] = {'false', 'true', ''},
    ['ragged-right'] = {'default', 'true', 'false', ''},
        ['noragged-right'] = {'default', _opt.is_neg},
    ['relative'] = {'false', _opt.is_num},
        ['norelative'] = {'default', _opt.is_neg},
    ['showfailed'] = {'false', 'true' ,''},
    ['staffsize'] = {'0', _opt.is_dim},
        ['inline-staffsize'] = {'0', _opt.is_dim},
    ['system-count'] = {'0', _opt.is_dim},
    ['tmpdir'] = {'tmp-ly'},
    ['twoside'] = {'\ly@istwosided', 'false', 'true', ''},
    ['verbatim'] = {'false', 'true', ''},
    ['voffset'] = {'0pt', _opt.is_dim},
    ['valign'] = {'center', 'top', 'bottom'},
    ['write-headers'] = {'false'},
    % MusicXML options
    ['absolute'] = {'false', 'true', ''},
    ['language'] = {'false'},
    ['lxml'] = {'false', 'true'},
    ['no-articulation-directions'] = {'true', 'false', ''},
    ['no-beaming'] = {'true', 'false', ''},
    ['no-page-layout'] = {'true', 'false', ''},
    ['no-rest-positions'] = {'true', 'false', ''},
    ['verbose'] = {'false', 'true', ''},
    ['xml2ly'] = {'musicxml2ly'},
  })
}
\directlua{
  ly = require(kpse.find_file("lyluatex.lua") or "lyluatex.lua")
  ly.make_list_file()
  if lua_options.client('ly').cleantmp then
    luatexbase.add_to_callback('stop_run', ly.clean_tmp_dir, 'lyluatex cleantmp')
    luatexbase.add_to_callback('stop_run', ly.conclusion_text, 'lyluatex conclusion')
  end
}
\catcode`-=12

%\directlua{ly.TWOSIDE = 'f'}

\newcommand{\ly@setunits}{%
  \let\ly@old@in\in\protected\def\in{in}%
  \let\ly@old@pt\pt\protected\def\pt{pt}%
  \let\ly@old@mm\mm\protected\def\mm{mm}%
  \let\ly@old@cm\cm\protected\def\cm{cm}%
  \let\ly@old@hfuzz\hfuzz\setlength{\hfuzz}{\maxdimen}%
}
\newcommand{\ly@resetunits}{%
  \let\in\ly@old@in%
  \let\pt\ly@old@pt%
  \let\mm\ly@old@mm%
  \let\cm\ly@old@cm%
  \setlength{\hfuzz}{\ly@old@hfuzz}%
}

% How the filename of a score will look like (if printed)
\newcommand{\lyFilename}[1]{\noindent #1\par\bigskip}

% Appearance of verbatim 'intertext' (if printed)
\newcommand{\lyIntertext}[1]{\noindent #1\par\bigskip}

% Appearance of LilyPond version (if printed)
\newcommand{\lyVersion}[1]{\noindent {\footnotesize\emph{(GNU LilyPond #1)}\par}\bigskip}

% Retrieve the three main font families (rm, sf, tt)
% and store them as options. Additionally store the
% *current* font for optional use.
\newcommand{\ly@currentfonts}{%
  \begingroup%
    \setluaoption{ly}{current-font}{%
      \directlua{ly.get_font_family(font.current())}
    }
    \rmfamily \edef\rmfamilyid{\fontid\font}%
    \sffamily \edef\sffamilyid{\fontid\font}%
    \ttfamily \edef\ttfamilyid{\fontid\font}%
    % Set font families to those of the document
    % that haven't been set explicitly as options.
    \directlua{ly.set_fonts(\rmfamilyid, \sffamilyid, \ttfamilyid)}%
  \endgroup%
}

% Main commands
% Score processing
\newcommand*{\ly@compilescore}[1]{%
  \ly@setunits%
  \setluaoption{ly}{currfiledir}{\currfiledir}
  \setluaoption{ly}{twoside}{\ly@istwosided}
  \directlua{
    #1
    ly.newpage_if_fullpage()
  }%
  \ly@resetunits%
  \ly@currentfonts%
  \directlua{ly.score:process()}%
}

% Inclusion of a .ly file
\newcommand*\includely[2][]{%
  \directlua{ly.state = 'file'}%
  \ly@compilescore{ly.file(
    '\luatexluaescapestring{#2}', [[#1]]
  )}%
}

% Inclusion of a musicxml file
\newcommand*\musicxmlfile[2][]{%
  \directlua{ly.state = 'file'}%
  \ly@compilescore{ly.file_musicxml(
    '\luatexluaescapestring{#2}', [[#1]]
  )}
}

% Base environments to include a LilyPond fragment integrated into
% the document.
\newcommand\lyscorebegin{\directlua{ly.buffenv_begin()}}
\newcommand\lyscoreend{\directlua{ly.buffenv_end()}}
\newenvironment{ly@bufferenv}{%
  \directlua{
    ly.insert_inline = string.match([[\options]], 'insert.*inline')
    if ly.insert_inline then
      if ly.varwidth_available then
        tex.print([[
          \string\begin{varwidth}{\string\linewidth}
        ]])
      else
        ly.insert_inline = false
        ly.err(
          [[You have required 'insert=inline' with lilypond environment,
          but package 'varwidth' wasn't found; either install it, or disable
          this option.]]
        )
      end
    end
  }
  \lyscorebegin%
}{%
  \lyscoreend%
  \ly@compilescore{ly.fragment(ly.score_content, [[\options]])}%
  \hspace{0pt}\\
  \directlua{
    if ly.insert_inline then tex.print([[\string\end{varwidth}]]) end
  }%
}

\NewEnviron{ly@compilely}{%
  \ly@compilescore{ly.fragment(
    '\luatexluaescapestring{\unexpanded\expandafter{\BODY}}',
    [[\options]]
  )}%
}

% Commands to print verbatim content of the score
\newcommand\lysetverbenv[2]{%
  \directlua{ly.verbenv = {
    '\luatexluaescapestring{\detokenize{#1}}',
    '\luatexluaescapestring{\detokenize{#2}}'
  }}%
}

% Environments to record custom headers and footers to be included in fragments
\newenvironment{lysavefrag}[1]{%
  \edef\filename{#1}
  \lyscorebegin%
}{%
  \lyscoreend%
  \directlua{ly.write_to_file('\filename'..'.ly', table.concat(ly.score_content,'\string\n'))}%
}

% Commands to transform or define lilypond environments so that it isn't necessary to add empty [].
\def\lyenv#1{%
  \expandafter\let\csname ly@env@#1\expandafter\endcsname\csname #1\endcsname%
  \expandafter\let\csname ly@env@end#1\expandafter\endcsname\csname end#1\endcsname%
  \expandafter\def\csname #1\endcsname{\IfNextToken[{\csname ly@env@#1\endcsname}{\csname ly@env@#1\endcsname[]}}%
}
\long\def\lynewenvironment#1{\@ifnextchar[{\ly@newenv@a{#1}}{\ly@newenv@a{#1}[0]}}
\long\def\ly@newenv@a#1[#2]{\@ifnextchar[{\ly@newenv@b{#1}{#2}}{\ly@newenv@b{#1}{#2}[]}}
\long\def\ly@newenv@b#1#2[#3]#4#5{%
  \newenvironment{#1}[#2][#3]{#4}{#5}
  \lyenv{#1}
}

% Parametrized command and environment for included LilyPond fragment
\lynewenvironment{ly}[1][noarg]{%
  \edef\options{#1}%
  \directlua{ly.state = 'env'}%
  \ly@bufferenv%
}{%
  \endly@bufferenv%
}


\newcommand{\lily}[2][]{%
  \edef\options{#1}%
  \let\ly@oldrepeat\repeat\def\repeat{}% Fix #51
  \directlua{ly.state = 'cmd'}%
  \begin{ly@compilely}%
    #2
  \end{ly@compilely}%
  \let\repeat\ly@oldrepeat%
}

\newcommand{\lyscore}[1]{\directlua{
  local i = tonumber('#1') or '#1'
  if i == '' then i = 1 end
  tex.sprint(ly.score[i] or '')
}}

% Commands for compatibility with lilypond-book
\let\lilypondfile\includely%
\protected\def\lilypond{%
  \def\reserved@a{lilypond}%
  \ifx\reserved@a\@currenvir\expandafter\ly%
  \else\expandafter\lily\fi%
}%
\let\endlilypond\endly
