\ExplSyntaxOn
\def\packagename{
  %% Name:
  seatingchart
}
\def\packagedate{
  %% Date:
  2025-07-25
}
\def\packageversion{
  %% Version:
  v0.5.0
}
\ExplSyntaxOff
  %% Author:      Matthias Werner
  %% Description: Seatings schemes 
  %% License:     LPPL 1.3 or later
\NeedsTeXFormat{LaTeX2e}[2022-06-01]

\ProvidesPackage{\packagename}[\packagedate\space \packageversion\space
Generation of seating plans]
%\RequirePackage{translator}
\IfFormatAtLeastTF{2022/06/01}{}{
  \PackageError{\packagename}{
    \packagename requires at least the TeX format \MessageBreak
    from 2022/06. \MessageBreak
  }{Update your LaTeX.}
}
\RequirePackage{etoolbox}
\ExplSyntaxOn
\sys_if_engine_luatex:TF{}{
 \PackageError{\packagename}{
   LuaLaTeX~is~required~to~use~this~package.\MessageBreak
   Sorry!
}{Use~LuaLaTex.} 
}
\RequirePackage{luacode}
\ExplSyntaxOff
\newtoggle{sc@rectshapt}\toggletrue{sc@rectshapt}
\newtoggle{sc@rnleft}
\newtoggle{sc@rnright}
\newtoggle{sc@room}
\DeclareKeys[seatingchart]{
  shape/arc.code:n = {\togglefalse{sc@rectshapt}},
  shape/rectangle.code:n = {\toggletrue{sc@rectshapt}},
  shape.usage = preamble,
  shape.initial:n = rectangle,
  rows.code:n = {
    \ifdef{\sc@rows}{
      \ifnumcomp{\sc@rows}{>}{0}{
        \PackageWarningNoLine{\packagename}{Overwriting number of rows.\MessageBreak
          You propably provided both, a room *and*\MessageBreak layout options.\MessageBreak
          Prepare for trouble}
      }{}
    }{}
    \gdef\sc@rows{#1}
  },
  rows.usage = preamble,
  rows.initial:n = 0,
  seats per row.code = {
    \ifdef{\sc@seatsperrow}{
      \ifnumcomp{\sc@seatsperrow}{>}{0}{
        \PackageWarningNoLine{\packagename}{Overwriting seats per
          row.\MessageBreak
          You propably provided both, a room *and*\MessageBreak layout options.\MessageBreak
          Prepare for trouble}
      }{}
    }{}
    \gdef\sc@seatsperrow{#1}
  },
  layout.store = \scroomfile,
  layout.initial:n =tu-chemnitz,
  layout.usage=load,
  seats per row.usage = preamble,
  seats per row.initial:n = 0,
  room.choice:,
  room/unknown.code:n={
    \PackageError{\packagename}{
      Room #1 is not in the layout file ' \packagename-\scroomfile.sc'}{%
      Check if you use the the correct layout file and\MessageBreak
      whether the room name is correct,\MessageBreak
      or define an own room.}
  },
  room.usage = load,
  init.code:n={
    \toggletrue{sc@room}
    \scInitSeating
  },
  init.usage = load,
  remove.code:n = {%
    \scRemoveSeats{#1}
  },
  remove.usage = preamble,
  shape.choice:,
  aisle.code:n={%
    \keySetAisle{#1}
  },
  blackboard.if = sc@blackboard,
  blackboard.initial:n = false,
  rownumbers.choice:,
  rownumbers/left.code = { \toggletrue{sc@rnleft} },
  rownumbers/right.code = { \toggletrue{sc@rnright} },
  rownumbers/both.code:n = {
    \toggletrue{sc@rnleft}\toggletrue{sc@rnright}
  },
  rownumbers/none.code:n = {
    \togglefalse{sc@rnleft}\togglefalse{sc@rnright}
  },
  rownumbers.initial:n=none,
  seat neighbor distance.store =\sc@sdist@x,
  row distance.store =\sc@sdist@y,
  seat distance.meta:n = {
    seat neighbor distance=#1,
    row distance=#1
  },
  seat distance.initial:n=2pt,
  rownumbers.initial:n = none,
  rownumber distance.store= \sc@rnsep,
  rownumber distance.initial:n = 2pt,
  rownumber font.store = \sc@rnfont,
  rownumber font.initial:n = \tiny,
  rownumber color.store = \sc@rncolor,
  rownumber color.initial:n = darkgray,
  empty seat background color.store =\sc@sc@empty,
  empty seat background color.initial:n = lightgray!20,
  empty seat border color.store = \sc@sc@emptyborder,
  empty seat border color.initial:n = lightgray,
  empty seat label color.store = \sc@sc@emptytext,
  empty seat label color.initial:n = lightgray!30,
  empty seat label font.store = \sc@s@emptyfont,
  assigned seat background color.store = \sc@sc@assigned,
  assigned seat background color.initial:n = lightgray!30,
  assigned seat border color.store = \sc@sc@assignedborder,
  assigned seat border color.initial:n = black,
  assigned seat label color.store = \sc@sc@assignedtext,
  assigned seat label color.initial:n = black,
  assigned seat label font.store = \sc@s@assignedfont,
  seat background color.meta:n = {
    empty seat background color=#1,
    assigned seat background color =#1
  },
  seat label color.meta:n = {
    empty seat label color = #1,
    assigned seat label color = #1
  },
  seat border color.meta:n = {
    empty seat border color = #1,
    assigned seat border color = #1
  },
  seat label font.initial:n = \small,
  seat label font.meta:n = {
    empty seat label font=#1,
    assigned seat label font = #1
  }
}
\NewDocumentCommand{\scDeclareRoom}{m m}{%
  \DeclareKeys[seatingchart]{room/#1.meta:n={#2}}%
}
\NewDocumentCommand{\scAliasRoom}{m m}{%
  \DeclareKeys[seatingchart]{room/#1.meta:n={room/#2}}%
}
\NewDocumentCommand{\scRemoveSeatAt}{m m}{
  \luadirect{removeSeatAt(#1,#2)}
}
\NewDocumentCommand{\sc@removeSeat}{>{\SplitArgument{1}{,}}m}{
  \scRemoveSeatAt#1
}
\NewDocumentCommand{\scRemoveSeats}{>{\SplitList{,}} m}{
  \ProcessList{#1}{\sc@removeSeat}
} 
\NewDocumentCommand{\scAssignSeatAt}{m m m}{
  \luadirect{assignSeatAt(#1,#2,\luastring{#3})}
}
\NewDocumentCommand{\sc@ParseRows}{m}{
}
\def\sc@parserange#1-#2\relax{%
  \def\@firstval{\ifx\relax#1\relax1\else#1\fi}%
  \def\@lastval{\ifx\relax#2\relax\sc@rows\else#2\fi}%
}
\newcommand{\keySetAisle}[1]{
  \scSetAisle[-]{#1}
}
\NewDocumentCommand{\scSetAisle}{O{-}m}{%
  \begingroup
  \edef\@tmp{#1}%
  \expandafter\sc@parserange\@tmp\relax
  \luadirect{removeAisle(#2,\@firstval,\@lastval)}
  \endgroup
}
\NewDocumentCommand{\scInitSeating}{o}{
  \IfValueT{#1}{}
  \luadirect{require("seatingchart.lua")}
  \iftoggle{sc@rectshapt}{
    \luadirect{initSeating(\sc@rows,\sc@seatsperrow,\luastring{rect})}
  }{
  \luadirect{initSeating(\sc@rows,\sc@seatsperrow,\luastring{arc})}
  }
}

\InputIfFileExists{seatingchart-\scroomfile.sc}{}{
  \PackageError{\packagename}{
    Can't open room layout file 'seatingchart-\scroomfile.sc'}{If you've provided a layout file, check whether it is
    in the path.}
}

% defaults
\ProcessKeyOptions

\NewDocumentCommand{\scConfig}{m}{%
  \SetKeys[sceating]{#1}%
  \sc@configtikz%
}
\ExplSyntaxOn
\newcommand\sc@configtikz{%
  \tikzset{%
    empty~seat/.style={
      draw=\sc@sc@emptyborder,
      fill=\sc@sc@empty,
      font=\sc@s@emptyfont,
      text=\sc@sc@empty,
      inner~sep=0pt
    },
    empty~label/.style={
      text=\sc@sc@emptytext,
      font=\sc@s@emptyfont
    },
    assigned~seat/.style={
      draw=\sc@sc@assignedborder,
      fill=\sc@sc@assigned,
      text=\sc@sc@assigned,
      font=\sc@s@assignedfont,
      inner~sep=0pt
    },
    assigned~label/.style={
      text=\sc@sc@assignedtext,
      font=\sc@s@assignedfont
    },
    rownumber/.style={
      font=\sc@rnfont,
      text=\sc@rncolor,
      anchor=east
    }%
  }%
}
\ExplSyntaxOff
\newlength\scseatwidth%\settowidth{\scseatwidth}{xxxx}%
\newlength\scseatheight%\settoheight{\scseatheight}{Xy}%
\DeclareKeys[scdrawing]{
  seat width.code:n={%
    \setlength{\scseatwidth}{#1}%
  },
  seat height.code:n={%
    \setlength{\scseatheight}{#1}%
  }
}
\ExplSyntaxOn
\NewDocumentCommand{\scDrawSeating}{o}{%
  \par\noindent%
  \IfValueTF{#1}{% 
    \SetKeys[scdrawing]{#1}%
  }{%
    \newlength\scremainingspace%
    \newlength\scrnlength%
    \setlength{\scremainingspace}{\linewidth}%
    \settowidth{\scrnlength}{\scrownumformat{99}}%
    %\typeout{Linewidth: \the\linewidth~(\the\scremainingspace)}
    %\typeout{Numswidth: \the\scrnlength~(seats:~\sc@seatsperrow)}
    %\typeout{********* Remaining start: \the\scremainingspace}%
    \iftoggle{sc@rnleft}{\addtolength{\scremainingspace}{-\dimeval{\scrnlength+\sc@rnsep}}}{}%
    %\typeout{********* Remaining~left~considered: \the\scremainingspace}%
    \iftoggle{sc@rnright}{\addtolength{\scremainingspace}{-\dimeval{\scrnlength+\sc@rnsep}}}{}%
    %\typeout{********* Remaining~right~considered: \the\scremainingspace}%
    \setlength{\scseatwidth}{\dimeval{\scremainingspace/(\sc@seatsperrow) - \sc@sdist@x}}
    %\typeout{Seatwidth: \the\scseatwidth}
    %\typeout{total: \dimeval{(\scseatwidth+2pt)*\sc@seatsperrow + \scrnlength*2 + \sc@rnsep*2}}
    %\typeout{********* Total: \the\pagetotal\ Goal: \the\pagegoal\ Height: \the\textheight}%
    \ifdimequal{\pagetotal}{0pt}{%
      \setlength{\scremainingspace}{\textheight}%
    }{%
      \setlength{\scremainingspace}{\dimeval{\pagegoal-\pagetotal}}%
    }%
    %\typeout{********* Total: \the\pagetotal\ Goal: \the\pagegoal\ Height: \the\textheight}%
    %\typeout{********* Remaining: \the\scremainingspace\ Width: \the\textwidth\ Height: \the\textheight}%
    \ifsc@blackboard%
      \setlength{\scseatheight}{\dimeval{\scremainingspace/(\sc@rows + 2) - \sc@sdist@y}}%
    \else%
      \setlength{\scseatheight}{\dimeval{\scremainingspace/\sc@rows - \sc@sdist@y}}%
    \fi%
    % A seat that is deeper than it is wide looks a bit silly.
    \ifdimcomp{\scseatheight}{>}{\scseatwidth}{\setlength{\scseatheight}{\scseatwidth}}{}%
  }%
  \sc@configtikz% 
  %\typeout{*********~Seatwidth:~\the\scseatwidth, Seatheight:~\the\scseatheight}%
  \luadirect{seatDim(\luastring{\scseatwidth},\luastring{\scseatheight})}%
  \begin{tikzpicture}[x=\scseatwidth+\sc@sdist@x,y=\scseatheight+\sc@sdist@y]
      \ifsc@blackboard%
         \node[rectangle, draw,minimum~width=0.4\textwidth] at (0,-2) {Tafel};
      \fi
      \iftoggle{sc@rectshapt}{
        \iftoggle{sc@rnleft}{
          \foreach \r in {1,..., \sc@rows} {
            \node[rownumber,xshift=-\fpeval{\scseatwidth/2+\sc@rnsep}] at (\fpeval{(1-\sc@seatsperrow)/2},\r-1) {\scrownumformat{\r}};
          }
        }{}
        \iftoggle{sc@rnright}{
          \foreach \r in {1,..., \sc@rows} {
            \node[rownumber,xshift=\fpeval{\scseatwidth/2+\sc@rnsep+\scrnlength}] at (\fpeval{(\sc@seatsperrow-1)/2},\r-1) {\scrownumformat{\r}};
          }
        }{}
      }{}
      \luadirect{drawSeats()}
    \end{tikzpicture}
}

\newcount\scnumofseats
\newtoggle{scfoundassigned}

\NewDocumentCommand{\scSeatingList}{s o m}{
  \luadirect{getNumberOfSeats()}
  \togglefalse{scfoundassigned}
  \loop
  \luadirect{setupNextlabel(\the\scnumofseats)}\relax
  %\typeout{ i=\the\scnumofseats}
  \iftoggle{scfoundassigned}{
        \setbox0=\hbox{\scnextlabel}
        \luadirect{saveTextLabel(\the\scnumofseats)}
      }{}
  \advance \scnumofseats by -1
  \unless\ifnum \scnumofseats<2
  \repeat
  \IfBooleanTF{#1}{
    \def\sccoor{true}
  }{
    \def\sccoor{nil}
  }
  \IfValueTF{#2}{
     \luadirect{generateSeatList(\luastring{#3}, \luastring{#2}, \sccoor)}
   }{
     \luadirect{generateSeatList(\luastring{#3}, nil, \sccoor)}
   }
}
\ExplSyntaxOff

\DeclareKeys[scseatingscheme]{
  empty seat background color.store =\sc@sc@empty,
  empty seat border color.store = \sc@sc@emptyborder,
  empty seat label color.store = \sc@sc@emptytext,
  empty seat label font.store = \sc@s@emptyfont,
  assigned seat background color.store = \sc@sc@assigned,
  assigned seat border color.store = \sc@sc@assignedborder,
  assigned seat label color.store = \sc@sc@assignedtext,
  assigned seat label font.store = \sc@s@assignedfont,
  seat background color.meta:n = {
    empty seat background color=#1,
    assigned seat background color =#1
  },
  seat label color.meta:n = {
    empty seat label color = #1,
    assigned seat label color = #1
  },
  seat border color.meta:n = {
    empty seat border color = #1,
    assigned seat border color = #1
  },
  seat label font.meta:n = {
    empty seat label font=#1,
    assigned seat label font = #1
  },
  row sep.store = \tss@rowsep,
  row sep.initial:n = 2,
  start row.store = \tss@startrow,
  start row.initial:n=1,
  end row.store = \tss@endrow,
  end row.initial:n = \sc@rows,
  row restart after.store= \tss@rowrestart,
  row restart after.initial:n = 100,%should be sufficient
  aisle counts.store = \tss@aislecnt,
  aisle counts.initial:n = 1,
  aisle restarts scheme.choice:,
  aisle restarts scheme/true.store=\tss@aislerestart,
  aisle restarts scheme/false.store=\tss@aislerestart,
  aisle restarts scheme.default:n = true,
  aisle restarts scheme.initial:n = false,
  ignore aisle.meta:n = {
    aisle counts=0
  },
  rtol.choice:,
  rtol/true.store = \tss@rtol,
  rtol/false.store = \tss@rtol,,
  rtol.default:n = true,
  rtol.initial:n = false,
  ignore removed seats.choice:,
  ignore removed seats/true.store=\tss@ignoreremoved,
  ignore removed seats/false.store=\tss@ignoreremoved,
  ignore removed seats.default:n = true,
  ignore removed seats.initial:n = false,
  assigned seat label.store=\tss@as@format,
  assigned seat label.initial:n=m{{\,}}D, 
  pattern.store=\tss@pattern,
  scheme.choice:,
  scheme/1x1.meta:n={
    row sep=1,
    pattern=X,
  },
  scheme/all.meta:n={
    scheme=1x1
  },
  scheme/2x2.meta:n={
    row sep=2,
    pattern=X-,
    aisle restarts scheme=true,
    row restart after=100
  },
  scheme/simple.meta:n={
    scheme=2x2
  },
  scheme/2x3.meta:n={
    row sep=2,
    pattern=X--,
    aisle restarts scheme=true,
    row restart after=100
  },
  scheme/sixpack.meta:n={
    scheme=2x3
  },
  scheme/2x3-.meta:n={
    row sep=2,
    pattern=X--,
    aisle restarts scheme=true,
    row restart after=3
  },
  scheme/2x4.meta:n={
    row sep=2,
    pattern=X---,
    aisle restarts scheme=true,
    row restart after=100
  },
  scheme/3x4.meta:n={
    row sep=3,
    pattern=X---,
    aisle restarts scheme=true,
    row restart after=100
  },
  scheme/sixpack-.meta:n={
    row sep=2,
    pattern=X--,
    aisle restarts scheme=true,
    row restart after=3
  },
  scheme/2x2-.meta:n={
    row sep=2,
    pattern=X-,
    aisle restarts scheme=true,
    row restart after=3
  },
  scheme/dense.meta:n={
    scheme=2x2-
  }
}

\NewDocumentCommand{\scConfigScheme}{m}{
  \SetKeys[scseatingscheme]{#1}
  
}

\NewDocumentCommand{\scSeatingScheme}{s o m}{
  \IfValueT{#2}{%
    \SetKeys[scseatingscheme]{#2}
  }%
  \IfBooleanTF{#1}{  %
    \SetKeys[scseatingscheme]{pattern=#3}
  }{%
    \SetKeys[scseatingscheme]{scheme=#3}
  }%
  \def\scpolicy{
    {["aisle~restarts"]=\tss@aislerestart,
      ["rtol"]=\tss@rtol,
      ["aisle counts"]=\tss@aislecnt,
      ["aisle restarts scheme"]=\tss@aislecnt,
      ["ignore removed seats"]=\tss@ignoreremoved,
      ["row sep"]=\tss@rowsep,
      ["row restart"]=\tss@rowrestart,
      ["first row"]=\tss@startrow,
      ["last row"]=\tss@endrow,
      ["label text"]=\luastringO{\tss@as@format}
    }}
  %\typeout{POLICY=\scpolicy, startrow=\tss@startrow\ endrow=\tss@endrow}
  \luadirect{seatingSchemeInRows(\luastring{\tss@pattern},\scpolicy)}
}
\ExplSyntaxOn

\newtoggle{sc@formatother}
\newcommand\sc@format@label[6]{%
  \togglefalse{sc@formatother}
  % absolut row, arabic:  m
  % absolut row, alpha:   a 
  % absolut row, Alpha:   A
  % absolut row, roman:   y
  % absolut row, Roman:   Y
  % running row, arabic:  r
  % running row, alpha:   b
  % running row, Alpha:   B
  % running row, roman:   i
  % running row, Roman:   I
  % absolut seat, arabic: n
  % absolut seat, alpha:  c
  % absolut seat, Alpha:  C
  % absolut seat, roman:  x
  % absolut seat, Roman:  X
  % running seat, arabic: s
  % running seat, alpha:  d
  % running seat, Alpha:  D
  % running seat, roman:  j
  % running seat, Roman:  J
  % Label:                l
  \regex_match_case:nnTF{
    {\cB.}{#6}% match all groups
    {m}{#1}
    {a}{\int_to_alph:n{#1}}
    {A}{\int_to_Alph:n{#1}}
    {y}{\int_to_roman:n{#1}}
    {Y}{\int_to_Roman:n{#1}}
    {r}{#3}
    {b}{\int_to_alph:n{#3}}
    {B}{\int_to_Alph:n{#3}}
    {i}{\int_to_roman:n{#3}}
    {I}{\int_to_Roman:n{#3}}
    {n}{#2}
    {c}{\int_to_alph:n{#2}}
    {C}{\int_to_Alph:n{#2}}
    {x}{\int_to_roman:n{#2}}
    {X}{\int_to_Roman:n{#2}}
    {s}{#4}
    {d}{\int_to_alph:n{#4}}
    {D}{\int_to_Alph:n{#4}}
    {j}{\int_to_roman:n{#4}}
    {J}{\int_to_Roman:n{#4}}
    {l}{#5}
  }{#6}{}{%
    \PackageWarningNoLine{\packagename}{Unknown~formating~character '#6'}%
  }
}

\NewDocumentCommand{\scassignedlabelformat}{o m m m m m}{
  % #2: absolute row
  % #3: absolute seat
  % #4: running row
  % #5: running seat
  % #6: text
  \IfValueTF{#1}{%
    \def\sc@format{#1}%
  }{%
    \def\sc@format{#6}
  }%
  %\typeout{*** Format: \sc@format}
  \expandafter\tl_map_tokens:Nn{\sc@format}{\sc@format@label{#2}{#3}{#4}{#5}{#6}}
  
}
\NewDocumentCommand{\tucemptylabelformat}{m m m m m}{
  % #2: absolute row
  % #3: absolute seat
  % #4: running row
  % #5: running seat
  % #6: text
}
\ExplSyntaxOff

\newcommand{\scrownumformat}[1]{%
  #1%
}

\ifboolexpr{
       test {\ifnumcomp{\sc@rows}{<}{1}} or
       test {\ifnumcomp{\sc@seatsperrow}{<}{1}}
}{
  \PackageError{\packagename}{Invalid layout option:\MessageBreak
    number of rows or number of seats per row can't be smaller than 1}{
    Use correct layout options}
}{}

\RequirePackage{tikz}
\usetikzlibrary{shapes.geometric}
\iftoggle{sc@room}{}{%
      \scInitSeating%
}
