% Copyright 2022 by Qrrbrbirlbel
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Free Documentation License.
%

\unless\ifcsname tikzextset\endcsname
  \input tikzext-util.tex
\fi

\pgfqkeys{/pgf/calendar/ext}{
    style/.style={#1},
    yesterday/.value required,
    yesterday/.code={%
      \begingroup
        \pgfutil@tempcnta=\pgfcalendarifdatejulian\relax
        \advance\pgfutil@tempcnta by -1\relax
        \pgfcalendarjuliantodate{\pgfutil@tempcnta}{\pgfcalendarifdateyear}{\pgfcalendarifdatemonth}{\pgfcalendarifdateday}%
        \edef\pgfcalendarifdatejulian{\the\pgfutil@tempcnta}%
        \pgfcalendarjuliantoweekday\pgfutil@tempcnta\pgfutil@tempcntb
        \edef\pgfcalendarifdateweekday{\the\pgfutil@tempcntb}%
        \pgfcalendarjulianyeartoweek\pgfutil@tempcnta\pgfcalendarifdateyear\pgfutil@tempcntb
        \edef\pgfcalendarifdateweek{\the\pgfutil@tempcntb}%
        \pgfcalendar@launch@ifdate{#1}{%
          \def\pgf@cal@temp{\pgfcalendarmatchestrue}%
        }{%
          \let\pgf@cal@temp\pgfutil@empty
        }%
        \expandafter
      \endgroup\pgf@cal@temp
    },
    relative/.code 2 args={%
      \begingroup
        \pgfutil@tempcnta=\pgfcalendarifdatejulian\relax
        \advance\pgfutil@tempcnta by #1\relax
        \pgfcalendarjuliantodate{\pgfutil@tempcnta}{\pgfcalendarifdateyear}{\pgfcalendarifdatemonth}{\pgfcalendarifdateday}%
        \edef\pgfcalendarifdatejulian{\the\pgfutil@tempcnta}%
        \pgfcalendarjuliantoweekday\pgfutil@tempcnta\pgfutil@tempcntb
        \edef\pgfcalendarifdateweekday{\the\pgfutil@tempcntb}%
        \pgfcalendarjulianyeartoweek\pgfutil@tempcnta\pgfcalendarifdateyear\pgfutil@tempcntb
        \edef\pgfcalendarifdateweek{\the\pgfutil@tempcntb}%
        \pgfcalendar@launch@ifdate{#2}{%
          \def\pgf@cal@temp{\pgfcalendarmatchestrue}%
        }{%
          \let\pgf@cal@temp\pgfutil@empty
        }%
      \expandafter
    \endgroup\pgf@cal@temp
    },
    Jan/.code={\ifnum\pgfcalendarifdatemonth=1 \expandafter\pgfcalendarmatchestrue\fi},Jan/.value forbidden,
    Feb/.code={\ifnum\pgfcalendarifdatemonth=2 \expandafter\pgfcalendarmatchestrue\fi},Feb/.value forbidden,
    Mar/.code={\ifnum\pgfcalendarifdatemonth=3 \expandafter\pgfcalendarmatchestrue\fi},Mar/.value forbidden,
    Apr/.code={\ifnum\pgfcalendarifdatemonth=4 \expandafter\pgfcalendarmatchestrue\fi},Apr/.value forbidden,
    May/.code={\ifnum\pgfcalendarifdatemonth=5 \expandafter\pgfcalendarmatchestrue\fi},May/.value forbidden,
    Jun/.code={\ifnum\pgfcalendarifdatemonth=6 \expandafter\pgfcalendarmatchestrue\fi},Jun/.value forbidden,
    Jul/.code={\ifnum\pgfcalendarifdatemonth=7 \expandafter\pgfcalendarmatchestrue\fi},Jul/.value forbidden,
    Aug/.code={\ifnum\pgfcalendarifdatemonth=8 \expandafter\pgfcalendarmatchestrue\fi},Aug/.value forbidden,
    Sep/.code={\ifnum\pgfcalendarifdatemonth=9 \expandafter\pgfcalendarmatchestrue\fi},Sep/.value forbidden,
    Oct/.code={\ifnum\pgfcalendarifdatemonth=10 \expandafter\pgfcalendarmatchestrue\fi},Oct/.value forbidden,
    Nov/.code={\ifnum\pgfcalendarifdatemonth=11 \expandafter\pgfcalendarmatchestrue\fi},Nov/.value forbidden,
    Dec/.code={\ifnum\pgfcalendarifdatemonth=12 \expandafter\pgfcalendarmatchestrue\fi},Dec/.value forbidden,
    %
    leap year/.code={%
        \pgfutil@tempcnta=#1\relax
        \divide\pgfutil@tempcnta4
        \multiply\pgfutil@tempcnta4
        \ifnum\pgfutil@tempcnta=#1\relax
            \divide\pgfutil@tempcnta100
            \multiply\pgfutil@tempcnta100
            \ifnum\pgfutil@tempcnta=#1\relax
                \divide\pgfutil@tempcnta400
                \multiply\pgfutil@tempcnta400
                \ifnum\pgfutil@tempcnta=#1\relax
                    \pgfcalendarmatchestrue
                \fi
            \else
                \pgfcalendarmatchestrue
            \fi
        \fi},
    leap year/.default=\pgfcalendarifdateyear,
    between days/.value required,
    between days/.code args={#1and#2}{%
        \pgfutil@tempcnta=#1\relax
        \ifnum\pgfcalendarifdateday<\pgfutil@tempcnta\else
            \pgfutil@tempcnta=#2\relax
            \ifnum\pgfcalendarifdateday>\pgfutil@tempcnta\else
                \pgfcalendarmatchestrue\fi\fi},
    week of month/.code={%
        \pgfutil@tempcnta=#1\relax
        \multiply\pgfutil@tempcnta7
        \ifnum\pgfcalendarifdateday>\pgfutil@tempcnta\else
            \advance\pgfutil@tempcnta-7
            \ifnum\pgfcalendarifdateday>\pgfutil@tempcnta
                \pgfcalendarmatchestrue
            \fi
        \fi},
    week of month'/.code={%
        \pgfextcalendar@getlastYMX\pgfcalendarifdateyear\pgfcalendarifdatemonth\pgfutil@tempcnta
        \advance\pgfutil@tempcnta1
        \pgfutil@tempcntb=#1\relax
        \multiply\pgfutil@tempcntb7
        \advance\pgfutil@tempcnta-\pgfutil@tempcntb
        \ifnum\pgfcalendarifdateday<\pgfutil@tempcnta\else
            \advance\pgfutil@tempcnta+7
            \ifnum\pgfcalendarifdateday<\pgfutil@tempcnta
                \pgfcalendarmatchestrue
            \fi
        \fi},
    first/.code={\pgfutil@in@:{#1}\ifpgfutil@in@
        \pgfextcalendar@ifdate@first{}#1\pgf@stop\else
        \pgfextcalendar@ifdate@first{}1:#1\pgf@stop\fi},
    last/.code={\pgfutil@in@:{#1}\ifpgfutil@in@
        \pgfextcalendar@ifdate@first'#1\pgf@stop\else
        \pgfextcalendar@ifdate@first'1:#1\pgf@stop\fi},
    % logic
    not/.value required,
    not/.code=%
        \begingroup
            \let\pgf@cal@tempa\pgfutil@empty
            \pgfcalendar@launch@ifdate{#1}{}{\def\pgf@cal@tempa{\pgfcalendarmatchestrue}}%
        \expandafter\endgroup\pgf@cal@tempa,
    and/.value required,
    and/.code=% and = {<cond 1>, <cond 2>, <cond 3>, …}
      \begingroup
        \pgfcalendarmatchestrue
        \pgfqkeys{/pgf/calendar/ext/and}{#1}%
        \ifpgfcalendarmatches % is it still true?
          \expandafter\pgfutil@firstoftwo
        \else
          \expandafter\pgfutil@secondoftwo
        \fi
        {\def\pgf@cal@temp{\pgfcalendarmatchestrue}}%
        {\let\pgf@cal@temp\pgfutil@empty}%
      \expandafter\endgroup\pgf@cal@temp,
    and/.unknown/.code=% only inside the group of and/.code
      \ifpgfcalendarmatches
        \expandafter\pgfutil@firstofone
      \else
        \expandafter\pgfutil@gobble
      \fi
      {%
        \begingroup
          \pgfcalendar@launch@ifdate{\pgfkeyscurrentname={#1}}%
          {\let\pgf@cal@temp\pgfutil@empty}{\def\pgf@cal@temp{\pgfcalendarmatchesfalse}}
        \expandafter\endgroup\pgf@cal@temp
      },%
    calendar week of month/.default=1,
    calendar week of month'/.default=1,
    calendar week of month/.code={%
      \begingroup
        \pgfutil@tempcnta=\pgfcalendarifdateday\relax
        \advance\pgfutil@tempcnta by 5
        \advance\pgfutil@tempcnta by -\pgfcalendarifdateweekday\relax
        \divide\pgfutil@tempcnta by 7
        \advance\pgfutil@tempcnta by -#1\relax
        \expandafter\endgroup\expandafter
      \ifnum\the\pgfutil@tempcnta=-1
        \pgfcalendarmatchestrue
      \fi},
    calendar week of month'/.code={%
      \begingroup
        \pgfextcalendar@getlastYMX\pgfcalendarifdateyear\pgfcalendarifdatemonth\pgfutil@tempcnta
        \advance\pgfutil@tempcnta by -\pgfcalendarifdateday\relax
        \edef\pgfcalendarifdateday{\the\pgfutil@tempcnta}%
        \pgfutil@tempcnta=-\pgfcalendarifdateweekday\relax
        \advance\pgfutil@tempcnta by 5
        \edef\pgfcalendarifdateweekday{\the\pgfutil@tempcnta}%
        \let\pgf@cal@temp\pgfutil@empty
        \pgfcalendar@launch@ifdate{ext/calendar week of month={#1}}{\def\pgf@cal@temp{\pgfcalendarmatchestrue}}{}%
      \expandafter\endgroup\pgf@cal@temp}}

\def\pgfextcalendar@ifdate@first#1#2:#3\pgf@stop{%
    \pgfqkeys{/pgf/calendar/ext}{and={#3, week of month#1={#2}}}}
\def\pgfextcalendar@getlastYMX#1#2#3{% #1 = year, #2 = month, #3 := last day
    \begingroup
        \ifnum#2=2 % stupid February
            \pgfcalendarmatchesfalse
            \pgfqkeys{/pgf/calendar/ext}{leap year={#1}}%
            \ifpgfcalendarmatches
              #3=29
            \else
              #3=28
            \fi
        \else
            #3=\ifcase#2\relax\or
            31\or\or31\or30\or31\or30\or31\or31\or30\or31\or30\or31\fi
        \fi
        \edef\pgf@cal@temp{#3=\the#3\relax}
      \expandafter
    \endgroup\pgf@cal@temp}

%
% weeks
%
\def\pgfextcalendar@week@setup#1{%
  \pgfutil@IfUndefined{pgfextcalendar@week@#1}{%
    \begingroup
      \pgfcalendardatetojulian{#1-01-01}\pgfutil@tempcnta
      \pgfcalendarjuliantoweekday\pgfutil@tempcnta\pgfutil@tempcntb
      %
      % tempcnta holds the julian number for first day of the current year
      % tempcntb holds the weekday for the first day of the current year
      %
      % set tempcnta to the Monday of the week with first day of current year
      \advance\pgfutil@tempcnta by -\pgfutil@tempcntb
      %
      % if the first week starts at Fri, Sat or Sun, next week is the 1st week
      \ifnum\pgfutil@tempcntb>3\relax
        \advance\pgfutil@tempcnta by 7\relax
      \fi
      % setup macro for year with {Julian number for day of first week}{weekday of -01-01}
      \expandafter\xdef\csname pgfextcalendar@week@#1\endcsname{{\the\pgfutil@tempcnta}{\the\pgfutil@tempcntb}}%
    \endgroup
  }{}%
}

\def\pgfextcalendarjulianyeartoweek#1#2#3{\pgfextcalendarjulianyeartoweek@{#1}{#2}{#3}{\iftrue}}
\def\pgfextcalendarjulianyeartoweek@#1#2#3#4{%
  % #1 = julian date (count)
  % #2 = year
  % #3 = count that holds the week at the end
  % #4 = \iftrue or \iffalse: whether week 53 needs to be checked (\iffalse when determining week from next year)
  \begingroup
    \pgfextcalendar@week@setup{#2}%
    #3=#1\relax
    %
    % calculate difference of days between current date and start of week 1
    %
    \advance#3 by -\expandafter\expandafter\expandafter\pgfutil@firstoftwo\csname pgfextcalendar@week@#2\endcsname\relax
    \ifnum#3<0\relax % whoops, we are in the week of the previous year
      \expandafter\pgfutil@firstoftwo
    \else
      \expandafter\pgfutil@secondoftwo
    \fi
    {% if first day of the year is Fri, Sat or Sun
      \ifnum\expandafter\expandafter\expandafter\pgfutil@secondoftwo\csname pgfextcalendar@week@#2\endcsname>3\relax
        \expandafter\pgfutil@firstoftwo
      \else
        \expandafter\pgfutil@secondoftwo
      \fi
      {% we need to check the week of the previous year
        #3=#2\relax
        \advance#3 by -1
        \edef\pgf@cal@temp{\noexpand\pgfextcalendarjulianyeartoweek@{#1}{\the#3}{#3}\noexpand\iffalse}%
        \pgf@cal@temp
      }{% yeah, it's weird
        \divide#3 by 7
        \advance#3 by 1
      }%
    }{%
      \divide#3 by 7
      \advance#3 by 1
      #4%
        \expandafter\pgfutil@firstofone
      \else
        \expandafter\pgfutil@gobble
      \fi
      {%
        \ifnum#3=53\relax % whoops, we are possibly in the first week of the next year
            \expandafter\pgfutil@firstofone
        \else
            \expandafter\pgfutil@gobble
        \fi
        {%
          \begingroup
            % check whether we're already in week 1 of the next year
            #3=#2\relax
            \advance#3 by 1
            \expandafter\pgfextcalendar@week@setup\expandafter{\the#3}%
            \ifnum#1<\expandafter\expandafter\expandafter\pgfutil@firstoftwo\csname pgfextcalendar@week@\the#3\endcsname\relax
              #3=53
            \else
              #3=1
            \fi
            \expandafter
          \endgroup\expandafter#3\the#3\relax
        }%
      }%
    }%
    \expandafter
  \endgroup\expandafter
  #3\the#3\relax
}

%
% shorthands for weeks (n)
%
% n-: shortest
% n=: shortest but prepends whitespace
% n0: leading zero
%
\expandafter\def\csname pgfcalendar@shorthand@n-\endcsname{%
  \if0\pgfextcalendarcurrentweek\else\pgfextcalendarcurrentweek\fi}
\expandafter\def\csname pgfcalendar@shorthand@n=\endcsname{%
  {\pgfutil@tempcnta=\pgfextcalendarcurrentweek\relax\ifnum\pgfutil@tempcnta<10\relax\setbox0=\hbox{1}\kern\wd0\relax\fi\the\pgfutil@tempcnta}}
\expandafter\def\csname pgfcalendar@shorthand@n0\endcsname{%
  \pgfextcalendarcurrentweek}

\let\tikzextorig@pgfcalendar\pgfcalendar
\long\def\pgfcalendar#1#2#3#4{%
  \tikzextorig@pgfcalendar{#1}{#2}{#3}{%
    \pgfextcalendarjulianyeartoweek{\pgfcalendarcurrentjulian}{\pgfcalendarcurrentyear}{\pgfutil@tempcntb}%
    \edef\pgfextcalendarcurrentweek{\ifnum\pgfutil@tempcntb<10 0\fi\the\pgfutil@tempcntb}%
    #4%
  }%
}

%
% Patching original \pgfcalendar@local@ifdate
%
\tikzextutil@prefixtomacro\pgfcalendar@local@ifdate{%
  \let\pgfextcalendarifdateweekday=\pgfextcalendarcurrentweekday
}
%
% Overwriting original \pgfcalendarifdate
%

\let\tikzextorig@pgfcalendar@launch@ifdate\pgfcalendar@launch@ifdate
\def\pgfcalendar@launch@ifdate{%
  \pgfextcalendarjulianyeartoweek{\pgfcalendarifdatejulian}{\pgfcalendarifdateyear}{\pgfutil@tempcntb}%
  \edef\pgfextcalendarifdateweek{\ifnum\pgfutil@tempcntb<10 0\fi\the\pgfutil@tempcntb}%
  %
  \tikzextorig@pgfcalendar@launch@ifdate
}

\pgfqkeys{/pgf/calendar/ext/week}{.value required,.code={\ifnum#1=\pgfcalendarifdateweek\relax\expandafter\pgfcalendarmatchestrue\fi}}

% Overwriting shorthands of pgfcalendar
\expandafter\def\csname pgfcalendar@shorthand@d-\endcsname{%
  \if0\pgfcalendarcurrentday\else\pgfcalendarcurrentday\fi}
\expandafter\def\csname pgfcalendar@shorthand@m-\endcsname{%
  \if0\pgfcalendarcurrentmonth\else\pgfcalendarcurrentmonth\fi}

%%% Compatibility
\tikzextset{
  /tikz-ext/compat/add library={pgfcalendar-ext}{pre 0.6},
  compat/pgfcalendar-ext/warn/.append code={%
    \pgfutil@for\tikzext@temp:=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,%
      leap year,and,not,week of month,week of month',calendar week of month,%
      calendar week of month',yesterday,week\do{%
      \pgfkeys@expanded{\noexpand\tikzextutil@compatwarning@key
        {pgfcalendar-ext}{pre 0.6}{/pgf/calendar/\tikzext@temp}{/pgf/calendar/ext/\tikzext@temp}%
      }%
    }%
    \pgfkeyssetvalue{/pgf/calendar/leap year/.@def}{\pgfcalendarifdateyear}%
    \pgfkeyssetvalue{/pgf/calendar/calendar week of month/.@def}{1}%
    \pgfkeyssetvalue{/pgf/calendar/calendar week of month'/.@def}{1}%
    \tikzextutil@compatwarning@cmd{pgfcalendar-ext}{pre 0.6}
      {pgfcalendarjulianyeartoweek}\pgfextcalendarjulianyeartoweek\tikzextutil@gobblethree
      \def\pgfcalendarcurrentweek{\pgfextcalendarcurrentweek}% no warning possible
      \def\pgfcalendarifdateweek{\pgfextcalendarifdateweek}% no warning possible
  },
  compat/pgfcalendar-ext/pre 0.6/.append code={%
    \pgfutil@for\tikzext@temp:=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,%
      leap year,and,not,week of month,week of month',calendar week of month,%
      calendar week of month',yesterday,week\do{%
      \pgfkeysedef{/pgf/calendar/\tikzext@temp}{%
        \noexpand\pgfkeysvalueof{/pgf/calendar/ext/\tikzext@temp/.@cmd}##1\noexpand\pgfeov
      }%
      \let\pgfcalendarjulianyeartoweek\pgfextcalendarjulianyeartoweek
      \def\pgfcalendarcurrentweek{\pgfextcalendarcurrentweek}%
      \def\pgfcalendarifdateweek{\pgfextcalendarifdateweek}%
    }%
  },
  compat/pgfcalendar-ext/\pgfkeysvalueof{/tikz/ext/compat/pgfcalendar-ext}%
}
\endinput
