% \iffalse meta-comment
% Copyright (C) 2026 Andreas Willibald Weber
%
% 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 the license is available at
% https://www.latex-project.org/lppl.txt.
%
% This work has the LPPL maintenance status `maintained'. The Current
% Maintainer of this work is Andreas Willibald Weber.
%
% This file is part of the `kanban-board' package. The complete list of files
% constituting the work appears in the main source file kanban-board.dtx.
% \fi
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
% \subsection{Persistent configuration}
%
% Built-in palettes and the default column list are registered through the
% same mechanisms that documents use. Mode, date format, start date,
% reference date, column list, palette, semantic labels, and gap defaults
% then enter the dedicated |kanban/setup| key family. Package options are
% accumulated and passed through the same transaction, so package-level and
% document-level setup have identical validation and scope. Throughout this
% part, a package option or |\kbset| changes the source for later snapshots,
% whereas an environment key changes only the snapshot being constructed.
%
% \paragraph{Built-in palettes and default columns.}
% The default, muted, and monochrome palettes obey exactly the same
% validation and registry rules as user-defined palettes, because they are
% registered through the public command. The default palette is selected
% immediately so that a document with no configuration produces the
% documented colors. The default column list is installed through the
% persistent column selector for the same reason. Its spelling is the text
% shown in the headings, and its uppercase form is the lookup key.
%    \begin{macrocode}
\kanbandefinepalette{default}{%
  kanbanSlate,kanbanBlue,kanbanTeal,kanbanSage,
  kanbanAmber,kanbanClay,kanbanRose,kanbanPlum}
\kanbandefinepalette{muted}{kanbanMutedBlue,kanbanMutedGreen,
  kanbanMutedPurple,kanbanMutedBrown}
\kanbandefinepalette{monochrome}{black!85,black!75,black!65,black!55}
\kanban@selectpalette{default}
\kanban@selectcolumns{Backlog,Doing,Done}
%    \end{macrocode}
%
% \paragraph{Mode selection.}
% Visual mode is a constrained textual choice. The persistent selector
% records a global |light| or |dark| default and the board selector records
% the same choice locally. Unknown values keep the previous selection and
% are reported through the shared unknown-value message, which names the
% scope of the setting.
%    \begin{macrocode}
\newcommand\kanban@selectmode[1]{%
  \kanban@normalizechoice{#1}%
  \ifdefstring{\kb@choice}{light}{\gdef\kanban@mode{light}}{%
    \ifdefstring{\kb@choice}{dark}{\gdef\kanban@mode{dark}}{%
      \kanban@warning{unknown-value}{mode}{#1}}}}
\newcommand\kanban@selectboardmode[1]{%
  \kanban@normalizechoice{#1}%
  \ifdefstring{\kb@choice}{light}{\def\kanban@boardmode{light}}{%
    \ifdefstring{\kb@choice}{dark}{\def\kanban@boardmode{dark}}{%
      \kanban@warning{unknown-value}{board mode}{#1}}}}
%    \end{macrocode}
%
% \paragraph{Date-format canonicalization and selection.}
% Five canonical format names are supported together with a small set of
% readable aliases. Canonicalization is separated from assignment so that the
% same alias table serves persistent and board-local selection. A recognized
% value writes one of |us|, |english|, |german|, |iso|, or |ymd|. An unknown
% value leaves the result empty and is diagnosed by the selector.
%    \begin{macrocode}
\newcommand\kanban@canonicaldateformat[1]{%
  \kanban@normalizechoice{#1}%
  \def\kb@datechoice{}%
  \ifdefstring{\kb@choice}{us}{\def\kb@datechoice{us}}{}%
  \ifdefstring{\kb@choice}{american}{\def\kb@datechoice{us}}{}%
  \ifdefstring{\kb@choice}{english}{\def\kb@datechoice{english}}{}%
  \ifdefstring{\kb@choice}{uk}{\def\kb@datechoice{english}}{}%
  \ifdefstring{\kb@choice}{british}{\def\kb@datechoice{english}}{}%
  \ifdefstring{\kb@choice}{german}{\def\kb@datechoice{german}}{}%
  \ifdefstring{\kb@choice}{de}{\def\kb@datechoice{german}}{}%
  \ifdefstring{\kb@choice}{deutsch}{\def\kb@datechoice{german}}{}%
  \ifdefstring{\kb@choice}{iso}{\def\kb@datechoice{iso}}{}%
  \ifdefstring{\kb@choice}{ymd}{\def\kb@datechoice{ymd}}{}%
  \ifdefstring{\kb@choice}{year-first}{\def\kb@datechoice{ymd}}{}}
\newcommand\kanban@selectdateformat[1]{%
  \kanban@canonicaldateformat{#1}%
  \ifdefempty\kb@datechoice
    {\kanban@warning{unknown-value}{date format}{#1}}
    {\global\let\kanban@dateformat\kb@datechoice}}
\newcommand\kanban@selectboarddateformat[1]{%
  \kanban@canonicaldateformat{#1}%
  \ifdefempty\kb@datechoice
    {\kanban@warning{unknown-value}{board date format}{#1}}
    {\let\kanban@boarddateformat\kb@datechoice}}
%    \end{macrocode}
%
% \paragraph{Validated column-gap assignment.}
% A column gap must be a non-negative dimension. The persistent and
% board-local setters share the safe grammar of the deadline part, assign the
% accepted input to a candidate register, and compare the evaluated length
% with zero before committing it to the appropriate scope.
%    \begin{macrocode}
\newcommand\kanban@setcolumnsep[1]{%
  \kanban@validatedimensionTF{#1}{%
    \setlength\kanban@candidatecolumnsep{#1}%
    \ifdim\kanban@candidatecolumnsep<0pt
      \kanban@dimensionwarning{columnsep}{#1}%
    \else
      \global\kanban@columnsep=\kanban@candidatecolumnsep
    \fi
  }{%
    \kanban@dimensionwarning{columnsep}{#1}}}
\newcommand\kanban@setboardcolumnsep[1]{%
  \kanban@validatedimensionTF{#1}{%
    \setlength\kanban@candidatecolumnsep{#1}%
    \ifdim\kanban@candidatecolumnsep<0pt
      \kanban@dimensionwarning{board columnsep}{#1}%
    \else
      \kanban@boardcolumnsep=\kanban@candidatecolumnsep
    \fi
  }{%
    \kanban@dimensionwarning{board columnsep}{#1}}}
%    \end{macrocode}
%
% \paragraph{Materialization of the visual mode.}
% The mode name is semantic state. Rendering needs concrete text, surface,
% border, and default heading colors, which this function derives from the
% active board mode after all board options have been processed. The tint
% percentage used for optional card backgrounds also depends on the mode. A
% light surface needs only a touch of color, whereas a dark surface needs a
% stronger share to stay visible while keeping the text contrast. Metadata
% chip colors are absent here because their light styling is stable in both
% modes.
%    \begin{macrocode}
\newcommand\kanban@applymode{%
  \ifdefstring{\kanban@boardmode}{dark}{%
    \def\kanban@textcolor{white!88!black}%
    \def\kanban@cardcolor{kanbanInk}%
    \def\kanban@cardtint{20}%
    \def\kanban@bordercolor{white!18!black}%
    \def\kanban@headingtext{white}%
  }{%
    \def\kanban@textcolor{kanbanInk}%
    \def\kanban@cardcolor{white}%
    \def\kanban@cardtint{15}%
    \def\kanban@bordercolor{black!15}%
    \def\kanban@headingtext{white}%
  }}
%    \end{macrocode}
%
% \paragraph{Persistent and board-local semantic labels.}
% Priority and completion labels are customizable text, but an empty label
% would create an invisible chip with misleading spacing. Both setters reject
% blank input. The label identifier supplied by the key family selects the
% persistent or board-local macro by name.
%    \begin{macrocode}
\newcommand\kanban@setlabel[2]{%
  \ifblank{#2}
    {\kanban@warning{empty-label}{label}{#1}}
    {\csgdef{kanban@#1label}{#2}}}
\newcommand\kanban@setboardlabel[2]{%
  \ifblank{#2}
    {\kanban@warning{empty-label}{board label}{#1}}
    {\csdef{kanban@board#1label}{#2}}}
%    \end{macrocode}
%
% \paragraph{Nested label key families.}
% Label customization is expressed as a subordinate key list such as
% |labels={high=URGENT,done=FINISHED}|. Separate persistent and board-local
% families direct identical key names to setters with different assignment
% scope. Each family owns its unknown-key diagnostic, which states whether
% the mistake occurred in global setup or in a board override. The key name
% is passed by expansion, because \textsf{l3msg} shows message arguments
% without expanding them.
%    \begin{macrocode}
\ExplSyntaxOn
\keys_define:nn {kanban/setup-labels}
  {
    high .code:n = {\kanban@setlabel{high}{#1}},
    medium .code:n = {\kanban@setlabel{medium}{#1}},
    low .code:n = {\kanban@setlabel{low}{#1}},
    done .code:n = {\kanban@setlabel{done}{#1}},
    unknown .code:n =
      {\use:x {\msg_warning:nnnn {kanban-board}{unknown-key}
        {label}{\l_keys_key_str}}}
  }
\keys_define:nn {kanban/board-labels}
  {
    high .code:n = {\kanban@setboardlabel{high}{#1}},
    medium .code:n = {\kanban@setboardlabel{medium}{#1}},
    low .code:n = {\kanban@setboardlabel{low}{#1}},
    done .code:n = {\kanban@setboardlabel{done}{#1}},
    unknown .code:n =
      {\use:x {\msg_warning:nnnn {kanban-board}{unknown-key}
        {board~label}{\l_keys_key_str}}}
  }
%    \end{macrocode}
%
% \paragraph{The persistent setup key family.}
% The setup family is the single entry point for package options and for
% |\kanbansetup|. Immediate keys delegate to their validated selectors. Date
% values are staged rather than applied at once, because a |date-format| key
% appearing later in the same list must take effect before an ambiguous start
% or reference date is interpreted. Alias keys provide the compact and the
% legacy spellings without a second implementation.
%    \begin{macrocode}
\keys_define:nn {kanban/setup}
  {
    columns .code:n = {\kanban@selectcolumns{#1}},
    palette .code:n = {\kanban@selectpalette{#1}},
    date-format .code:n = {\kanban@selectdateformat{#1}},
    dateformat .meta:n = {date-format={#1}},
    startdate .code:n =
      {
        \tl_set:Nn \l__kanban_pending_startdate_tl {#1}
        \bool_set_true:N \l__kanban_pending_startdate_bool
      },
    reference-date .code:n =
      {
        \tl_set:Nn \l__kanban_pending_reference_date_tl {#1}
        \bool_set_true:N \l__kanban_pending_reference_date_bool
      },
    today .meta:n = {reference-date={#1}},
    mode .code:n = {\kanban@selectmode{#1}},
    columnsep .code:n = {\kanban@setcolumnsep{#1}},
    labels .code:n = {\keys_set:nn {kanban/setup-labels}{#1}},
    unknown .code:n =
      {\use:x {\msg_warning:nnnn {kanban-board}{unknown-key}
        {setup}{\l_keys_key_str}}}
  }
%    \end{macrocode}
%
% \paragraph{A deferred setup transaction.}
% Processing a setup list is a small transaction. It clears both staging
% flags, processes the complete list in source order, and only then applies
% any staged dates under the final selected format. Consequently
% |startdate=07/08/2026,date-format=english| and the reversed key order have
% the same interpretation.
%    \begin{macrocode}
\cs_new_protected:Npn \kanban@processsetup #1
  {
    \bool_set_false:N \l__kanban_pending_startdate_bool
    \bool_set_false:N \l__kanban_pending_reference_date_bool
    \keys_set:nn {kanban/setup}{#1}
    \bool_if:NT \l__kanban_pending_startdate_bool
      {\exp_args:NV \kanban@setstartdate
        \l__kanban_pending_startdate_tl}
    \bool_if:NT \l__kanban_pending_reference_date_bool
      {\exp_args:NV \kanban@setreferencedate
        \l__kanban_pending_reference_date_tl}
  }
\ExplSyntaxOff
%    \end{macrocode}
%
% \begin{macro}{\kanbansetup}
% \begin{macro}{\kbset}
% The canonical setup command exposes the transaction with one mandatory
% key-list argument. The short alias follows the same collision-tolerant
% policy as the palette alias. Equivalence by |\let| gives both names exactly
% the same argument parser and behavior.
%    \begin{macrocode}
\NewDocumentCommand\kanbansetup{m}{\kanban@processsetup{#1}}
\@ifundefined{kbset}
  {\let\kbset\kanbansetup}
  {\kanban@warning{alias-taken}{kbset}{kanbansetup}}
%    \end{macrocode}
%
% \paragraph{Collection and application of package options.}
% LaTeX2e presents package options one at a time. The package collects them
% into one token list and applies that list through the deferred setup
% transaction after option processing has finished. Package options
% therefore share validation, aliases, key-order independence, and
% diagnostics with |\kanbansetup|. The transaction runs while |kanban-board.sty|
% is still being read, so the line number at that moment points into the
% package rather than into the document. The application therefore runs in
% a group that redefines the line context of \textsf{l3msg} to name the
% package options. Every assignment made by the setup transaction is
% global, so the group does not shorten the lifetime of any setting.
%
% Formats dated 2021-06-01 or later parse raw option lists with the
% brace-aware key--value processor, so the catch-all collector receives every
% option intact, including values with embedded commas such as
% |columns={A,B}|. The 2020-10-01 format splits raw option lists at every
% comma and mishandles brace groups in its option bookkeeping. On such
% formats the package routes its options through \textsf{kvoptions}. Every
% documented setup key is declared as a \textsf{keyval} key whose handler
% re-wraps the brace-stripped value before appending it to the same
% accumulator, and unrecognized options fall through to the same catch-all
% collector. Unknown package options consequently produce the same warning
% as unknown setup keys on both paths.
%    \begin{macrocode}
\ExplSyntaxOn
\tl_new:N \g__kanban_package_options_tl
\cs_new_protected:Npn \kanban@collectpackageoption
  {
    \tl_if_empty:NF \g__kanban_package_options_tl
      {\tl_gput_right:Nn \g__kanban_package_options_tl {,}}
    \exp_args:NNo \tl_gput_right:Nn
      \g__kanban_package_options_tl {\CurrentOption}
  }
\cs_new_protected:Npn \kanban@collectkeyvalue #1#2
  {
    \tl_if_empty:NF \g__kanban_package_options_tl
      {\tl_gput_right:Nn \g__kanban_package_options_tl {,}}
    \tl_gput_right:Nn \g__kanban_package_options_tl {#1={#2}}
  }
\cs_new_protected:Npn \kanban@applypackageoptions
  {
    \tl_if_empty:NF \g__kanban_package_options_tl
      {
        \group_begin:
          \cs_set:Npn \msg_line_context: {in~the~package~options}
          \exp_args:NV \kanban@processsetup
            \g__kanban_package_options_tl
        \group_end:
      }
  }
\ExplSyntaxOff
\IfFormatAtLeastTF{2021-06-01}
  {%
    \DeclareOption*{\kanban@collectpackageoption}%
    \ProcessOptions\relax
  }
  {%
    \RequirePackage{kvoptions}%
    \SetupKeyvalOptions{family=kanban,prefix=kanban@pkgopt@}%
    \DeclareDefaultOption{\kanban@collectpackageoption}%
    \define@key{kanban}{columns}%
      {\kanban@collectkeyvalue{columns}{#1}}%
    \define@key{kanban}{palette}%
      {\kanban@collectkeyvalue{palette}{#1}}%
    \define@key{kanban}{date-format}%
      {\kanban@collectkeyvalue{date-format}{#1}}%
    \define@key{kanban}{dateformat}%
      {\kanban@collectkeyvalue{dateformat}{#1}}%
    \define@key{kanban}{startdate}%
      {\kanban@collectkeyvalue{startdate}{#1}}%
    \define@key{kanban}{reference-date}%
      {\kanban@collectkeyvalue{reference-date}{#1}}%
    \define@key{kanban}{today}%
      {\kanban@collectkeyvalue{today}{#1}}%
    \define@key{kanban}{mode}%
      {\kanban@collectkeyvalue{mode}{#1}}%
    \define@key{kanban}{columnsep}%
      {\kanban@collectkeyvalue{columnsep}{#1}}%
    \define@key{kanban}{labels}%
      {\kanban@collectkeyvalue{labels}{#1}}%
    \ProcessKeyvalOptions*\relax
  }
\kanban@applypackageoptions
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
