\ProvidesPackage{planning}

\usepackage[rgb]{xcolor}
\usepackage{tikz}


\usepackage[french]{babel}
\usetikzlibrary{babel} % pour pouvoir utiliser le babel français

% "key handler" pour stocker les longueurs, basé sur le `.store in` original
\pgfkeys{
  /handlers/.length in/.code={%
    \let#1\relax% si la longueur existe déjà, la réinitialiser
    \newlength{#1}% créer le nouveau registre de longueur
    \pgfkeysalso{\pgfkeyscurrentpath/.code=\pgfmathsetlength#1{##1}}% calculer et stocker la longueur
  }
}
% calcul de quelques longueurs supplémentaires
\newlength{\hour} % largeur d'une heure
\newlength{\minute} % largeur d'une minute
\newlength{\hday} % hauteur d'une journée
\newlength{\hgroupe} % hauteur d'un groupe
% le nombre de groupes doit être un entier > 0
% s'il y a un seul groupe, on ne l'affiche pas
\newcommand*\normalizenumberofgroups{
  \edef\numberofgroups{\the\numexpr\numberofgroups\relax}
  \ifnum\numberofgroups<1\relax
    \edef\numberofgroups{1}
  \fi
  \ifnum\numberofgroups=1\relax
    \pgfkeysalso{cacher etiquettes groupes}
  \fi
}
% recalcule les longueurs après changement des paramètres
\newcommand*\calculatelengths{
  \setlength{\hour}{\dimexpr\wplanning / \hoursperday\relax} % largeur d'une heure
  \setlength{\minute}{\dimexpr\hour / 60\relax} % largeur d'une minute
  \setlength{\hday}{\dimexpr\hplanning / \daysperweek\relax} % hauteur d'une journée
  \setlength{\hgroupe}{\dimexpr\hday / \numberofgroups\relax} % hauteur d'un groupe
}
% afficher les contraintes sur les réservations des salles ?
\newif\ifplanning@contraintes
% les paramètres généraux
\tikzset{
  % largeur du planning
  largeur du planning/.length in=\wplanning,
  largeur du planning = 21cm,
  % hauteur du planning
  hauteur du planning/.length in = \hplanning,
  hauteur du planning = 17cm,
  % largeur du nom du jour
  largeur du nom du jour/.length in = \wdaylabel,
  largeur du nom du jour = 4cm,
  % largeur du nom du groupe
  largeur du nom du groupe/.length in = \wgroupelabel,
  largeur du nom du groupe = 1cm,
  % hauteur du déplacement vertical des heures
  hauteur du deplacement vertical des heures/.length in = \hhouroffset,
  hauteur du deplacement vertical des heures = 4mm,
  % nombre de jours dans la semaine
  nombre de jours/.estore in=\daysperweek,
  nombre de jours = 5,
  % nombre d'heures par jour
  nombre d'heures/.estore in=\hoursperday,
  nombre d'heures=10,
  % première heure de la journée
  heure du debut/.estore in=\startinghour,
  heure du debut=8,
  % nombre de groupes
  nombre de groupes/.estore in=\numberofgroups,
  nombre de groupes/.append code=\normalizenumberofgroups,
  nombre de groupes=3,
  % les méthodes qui demandent le recalcule des longueurs
  largeur du planning/.append code=\calculatelengths,
  hauteur du planning/.append code=\calculatelengths,
  nombre de jours/.append code=\calculatelengths,
  nombre d'heures/.append code=\calculatelengths,
  nombre de groupes/.append code=\calculatelengths,
  % pour cacher la colonne avec les groupes
  % ce style est évoqué automatiquement par `nombre de groupes=1`
  cacher etiquettes groupes/.style={
    largeur du nom du groupe=0,
    etiquette groupe/.style={opacity=0},
    ligne groupe/.style={draw=none}
  },
  % afficher les contraintes (par defaut `false`) sur les créneaux
  % imposées par le service de réservation des salles
  contraintes/.is if=planning@contraintes,
  % il y a aussi les style optionnels utilisé avec /.try
  % etiquette jour
  % etiquette groupe
  % etiquette heure
  % etiquette demi-heure
  % ligne jour
  % ligne groupe
  % ligne heure
  % ligne demi-heure
  % lignes verticales
}
\calculatelengths

% définition du style `pos` qui peut être utilisé ainsi
% [pos={0}{1}{09}{10}] pour positionner un point ( , ) à la place de
%   lundi(0), à la séparation entre les groupes 1 et 2, à 9 heures et 10 minutes
% ces paramètres sont des entiers, car ils sont évalués avec `\numexpr`
\tikzset{
  every picture/.style={yscale=-1},
  pos/.style n args={4}{
    insert path={(\the\dimexpr \valhour\hour + \valmin\minute\relax,\the\dimexpr \valday\hday+\valgroupe\hgroupe\relax)}
  },
  pos/.prefix code={
    % Je n'utilise pas \pgfmathsetmacro car 09 (9 en base 8!) n'est pas un nombre valide.
    \edef\valday{\the\numexpr #1\relax}% day = 0 (lundi), ..., 5 (samedi)
    \edef\valgroupe{\the\numexpr #2\relax}% groupe = 0, ...
    \edef\valhour{\the\numexpr #3-\startinghour\relax}% hour = \startinghour, ...
    \edef\valmin{\the\numexpr #4\relax}% minutes = 0, ...
  }
}
% définition de la macro \horaires
% qui sauvegardes les heures et les minutes d'une expression comme 08:00-10:30
% dans \fromh, \fromm, \toh, \tom, puis calcule la longueur \duree
\newlength{\duree}
\def\horaires#1:#2-#3:#4;{%
  \edef\fromh{#1}%
  \edef\fromm{#2}%
  \edef\toh{#3}%
  \edef\tom{#4}%
  \setlength{\duree}{\dimexpr\toh\hour-\fromh\hour+\tom\minute-\fromm\minute\relax}
}
% \parsegroupelen est une macro utilitaire pour \parsegroupe
% le paramètre attendu est de la forme `2-3` ou `2`
% un paramètre `2` est équivalent à `2-2`
\def\parsegroupelen#1-#2-#3endparsegroupe{
  \ifx\hfuzz#1\hfuzz
    \edef\fromg{0}
    \edef\tog{1}
  \else
    \edef\fromg{#1-1}
    \ifx\hfuzz#2\hfuzz
      \edef\tog{#1}
    \else
      \edef\tog{#2}
    \fi
  \fi
}
% \parsegroupe est une macro utilitaire pour \groupe
% le paramètre attendu est de la forme `1/3` ou `1`
% un paramètre `1` est équivalent à `1/\numberofgroups`
% où \numberofgroups est le nombre de groupes par défaut
% établi par `nombre de groupes=`
\def\parsegroupe#1/#2/#3endparsegroupe{
  \parsegroupelen#1--endparsegroupe
  \ifx\hfuzz#2\hfuzz
    \edef\ngrps{\numberofgroups}
  \else
    \edef\ngrps{#2}
  \fi
}
% \groupe défini la position et la taille du créneau dans une journée
% le paramètre attendu est de la forme {k-l/m} où qui signifie :
% - k-l/m => groupe qui occupe la place des groupes k à l parmi m groupes
% k,l et m peuvent être manquant, et ainsi avoir des paramètres de la forme
% - {k/m} => groupe k parmi m groupes
% - {k} => groupe k parmi \numberofgroups groupes (équivalent à k-k)
% - {k-l} => groupe qui occupe la place des groupes k à l parmi \numberofgroups groupes
\def\groupe#1;{
  \parsegroupe#1//endparsegroupe
}
% les styles (couleurs, épaisseur des traits...) du planning
\tikzset{
  fond/.style = {draw,fill=module\arabic{module}},
  heure/.style = {inner sep=2pt,outer sep=0,draw,fill=black,fill opacity=.1,text opacity=1,scale=.7},
  heure debut/.style={heure, below right},
  heure fin/.style={heure, below left},
  scale factor calc/.code={
    \pgfmathsetmacro{\scalefactor}{1/(1+\numberofgroups/7)}% facteur ajusté en fonction du nombre de groupes
    \pgfmathsetmacro{\scaleinvfactor}{1/\scalefactor}
    \pgfkeysalso{scale={\scalefactor},inner sep=2*\scalefactor pt}%
  },
  titre/.style={
    % text width=\scaleinvfactor\duree,
    align=flush center,
    scale factor calc,
  },
  sur-titre/.style={
    above,
    font={\sffamily\hyphenchar\font=-1},
    % text width=\scaleinvfactor\duree,
    align=flush center,
    scale factor calc,
  },
  sous-titre/.style={
    below,
    black!84,
    font=\sffamily\it,
    % text width=\scaleinvfactor\duree,
    align=flush center,
    scale factor calc,
  },
  echelle/.style={
    titre/.append style={scale=#1},
    sur-titre/.append style={scale=#1},
    sous-titre/.append style={scale=#1},
  },
  plus haut/.style={titre/.append style={yshift=#1}},
}
% définition de \joursemaine
% qui renvoie le numéro du jour de la semaine en fonction du nom :
% lundi(1),mardi(2),mercredi(3),jeudi(4),vendredi(5),samedi(6),dimanche(7)
% ainsi par exemple \joursemaine{mercredi} retourne 3
\def\joursemaine#1#2#3;{%
  \ifx#1l 1\fi%
  \ifx#1m%%
    \ifx#2a 2\else 3\fi%
  \fi%
  \ifx#1j 4\fi%
  \ifx#1v 5\fi%
  \ifx#1s 6\fi%
  \ifx#1d 7\fi%
}
% définition de \creneau
% qui est la commande principale pour dessiner un créneau
% #1 est un style optionnel, par exemple {fond/.style={fill=red}}
% #2 est le "sur-titre"
% #3 est le "titre"
% #4 est le "sous-titre" utilisé en général pour la salle
% #5 est le jour de la forme "lundi" ou "lun" ou même "l"
% #6 est le groupe, de la forme {k}, {k-l}, {k/m} ...voir \groupe pour plus d'infos
% #7 sont les horaires, sous la forme {08:00-10:30} utilisée par \horaires
\newcommand{\creneau}[7][]{% \creneau[style]{surtitre}{titre}{sous-titre}{jour}{groupe}{horaires}
  \edef\jour{\joursemaine#5;}
  \horaires#7;
  \groupe#6;
  \edef\fromd{\jour-1}
  \edef\tod{\jour-1}
  \begin{scope}[nombre de groupes=\ngrps,#1]
    % définitions des coordonnées (start), (end) et (mid) du créneau
    \path[fond]
      [pos={\fromd}{\fromg}{\fromh}{\fromm}] coordinate (start)
      [pos={\tod}{\tog}{\toh}{\tom}] coordinate (end)
      (start) rectangle coordinate (mid) (end)
    ;
    % affiche les heures de début et de fin
    \path
      (start) node[heure debut]{\NoAutoSpacing\fromh:\fromm}
      (start-|end) node[heure fin]{\NoAutoSpacing\toh:\tom}
    ;
    % affiche le titre, le sur-titre et le sous-titre
    \path
      (mid) node[titre](module){#3}
      \ifx\hfuzz#2\hfuzz\else
        (module.north) node[sur-titre]{#2}
      \fi
      \ifx\hfuzz#4\hfuzz\else
        (module.south) node[sous-titre]{#4}
      \fi
    ;
  \end{scope}
}
% définition des couleurs des modules par défaut
\tikzset{
  screen colors 1/.code={
    \definecolor{module1}{HTML}{ffe119}
    \definecolor{module2}{HTML}{f58231}
    \definecolor{module3}{HTML}{bcf60c}
    \definecolor{module4}{HTML}{f032e6}
    \definecolor{module5}{HTML}{ffd8b1}
    \definecolor{module6}{HTML}{f7295B}
    \definecolor{module7}{HTML}{4cc45b}
    \definecolor{module8}{HTML}{46f0f0}
    \definecolor{module9}{HTML}{b13ed4}
    \definecolor{module10}{HTML}{ff9ece}
    \definecolor{module11}{HTML}{40c0c0}
    \definecolor{module12}{HTML}{e7beff}
    \definecolor{module13}{HTML}{dAa354}
    \definecolor{module14}{HTML}{fffac8}
    \definecolor{module15}{HTML}{c0c0c0}
    \definecolor{module16}{HTML}{aaffc3}
    \definecolor{module17}{HTML}{5383f8}
  },
  screen colors 2/.code={
    \definecolor{module1}{hsb}{ 0.44,0.576,0.931}
    \definecolor{module2}{hsb}{ 0.94,0.348,0.95}
    \definecolor{module3}{hsb}{ 0.19,0.424,0.938}
    \definecolor{module4}{hsb}{ 0.25,0.400,0.981}
    \definecolor{module5}{hsb}{ 0.69,0.548,0.988}
    \definecolor{module6}{hsb}{ 0.56,0.500,0.912}
    \definecolor{module7}{hsb}{ 0.75,0.376,0.962}
    \definecolor{module8}{hsb}{ 1   ,0.700,1}
    \definecolor{module9}{hsb}{ 0.62,0.452,0.975}
    \definecolor{module10}{hsb}{0.12,0.675,0.956}
    \definecolor{module11}{hsb}{0.88,0.700,0.969}
    \definecolor{module12}{hsb}{0.81,0.624,0.925}
    \definecolor{module13}{hsb}{0.38,0.476,0.919}
    \definecolor{module14}{hsb}{0.50,0.524,0.994}
    \definecolor{module15}{hsb}{0.31,0.652,0.944}
    \definecolor{module16}{hsb}{0.06,0.324,0.906}
    \definecolor{module17}{hsb}{   1,    1,    1}
  },
  screen colors 3/.code={
    \definecolor{module1}{hsb}{ 0.00,0.53,0.931}
    \definecolor{module2}{hsb}{ 0.33,0.77,0.95}
    \definecolor{module3}{hsb}{ 0.67,0.42,0.938}
    \definecolor{module4}{hsb}{ 0.17,0.65,0.981}
    \definecolor{module5}{hsb}{ 0.50,0.88,0.988}
    \definecolor{module6}{hsb}{ 0.83,0.36,0.912}
    \definecolor{module7}{hsb}{ 0.08,0.59,0.962}
    \definecolor{module8}{hsb}{ 0.42,0.82,1}
    \definecolor{module9}{hsb}{ 0.75,0.48,0.975}
    \definecolor{module10}{hsb}{0.25,0.71,0.956}
    \definecolor{module11}{hsb}{0.58,0.94,0.969}
    \definecolor{module12}{hsb}{0.92,0.57,0.925}
    \definecolor{module13}{hsb}{0.38,0.80,0.919}
    \definecolor{module14}{hsb}{0.71,0.34,0.994}
    \definecolor{module15}{hsb}{0.05,0.68,0.944}
    \definecolor{module16}{hsb}{0.55,0.92,0.906}
    \definecolor{module17}{hsb}{0.88,0.45,    1}
  },
  print colors/.code={
    \colorlet{module1}{black!3}
    \colorlet{module2}{black!21}
    \colorlet{module3}{black!5}
    \colorlet{module4}{black!11}
    \colorlet{module5}{black!7}
    \colorlet{module6}{black!14}
    \colorlet{module7}{black!1}
    \colorlet{module8}{black!9}
    \colorlet{module9}{black!19}
    \colorlet{module10}{black!4}
    \colorlet{module11}{black!18}
    \colorlet{module12}{black!8}
    \colorlet{module13}{black!13}
    \colorlet{module14}{black!20}
    \colorlet{module15}{black!14}
    \colorlet{module16}{black!14}
    \colorlet{module17}{black!14}
  },
  screen colors 3% les couleurs par défaut
}
% Les commandes pour afficher les contraintes de réservation des salles
% sous forme de bandes verticales :
% - les pauses obligatoires sont en vert
% - le créneau fixe est en rouge
\newcommand*{\draw@contraintes}{
  % pause obligatoires  (en vert)
  \fill[opacity=.1, green]
    % 10:00-10:15
    [pos={0}{0}{10}{0}] {[yshift=-.49\hhouroffset] coordinate (start)}
    [pos={\daysperweek}{0}{10}{15}] coordinate (end)
    (start) rectangle (end)
    % 12:15-12:45
    [pos={0}{0}{12}{15}] {[yshift=-.49\hhouroffset] coordinate (start)}
    [pos={\daysperweek}{0}{12}{45}] coordinate (end)
    (start) rectangle (end)
    % 14:45-15:00
    [pos={0}{0}{14}{45}] {[yshift=-.49\hhouroffset] coordinate (start)}
    [pos={\daysperweek}{0}{15}{00}] coordinate (end)
    (start) rectangle (end)
    % 16:30-16:45
    [pos={0}{0}{16}{30}] {[yshift=-.49\hhouroffset] coordinate (start)}
    [pos={\daysperweek}{0}{16}{45}] coordinate (end)
    (start) rectangle (end)
  ;
  % le créneau fixe (en rouge)
  \fill[opacity=.1, red]
    [pos={0}{0}{15}{0}] {[yshift=-.49\hhouroffset] coordinate (start)}
    [pos={\daysperweek}{0}{16}{30}] coordinate (end)
    (start) rectangle (end)
  ;
}
% l'environnement planning
% qui utilise tikzpicture
% #1 est le (tikz-)style optionnel du planning
\newenvironment{planning}[1][]
{
  \begin{tikzpicture}[#1]
    % si les contraintes sont activées, on les affiche
    \ifplanning@contraintes
      \draw@contraintes
    \fi
    % tracer les lignes horizontales
    \foreach \d in {0,...,\daysperweek}{
      \draw[ligne jour/.try] (-\wdaylabel-\wgroupelabel,\d*\hday) -- (\wplanning,\d*\hday);
      \ifnum\numberofgroups > 1\relax
        \ifnum\d < \daysperweek\relax
          \foreach \g in {1,...,\the\numexpr\numberofgroups-1\relax}
            \draw[very thin, ligne groupe/.try] (-\wgroupelabel,\d*\hday+\g*\hgroupe) -- (\wplanning,\d*\hday+\g*\hgroupe);
        \fi
      \fi
    }
    % tracer les lignes verticales
    \draw[lignes verticales/.try]
      (-\wdaylabel-\wgroupelabel,0) -- +(0,\hplanning)
      (-\wgroupelabel,0) -- +(0,\hplanning)
      (0,0) -- +(0,\hplanning)
      (\wplanning,0) -- +(0,\hplanning)
      ;
    % mettre les étiquettes verticales (jours et groupes)
    \foreach[count=\dn from 0] \d in {lundi,mardi,mercredi,jeudi,vendredi,samedi}{
      \ifnum\dn < \daysperweek\relax
        \node[scale=2,etiquette jour/.try] at (-.5\wdaylabel-\wgroupelabel,\dn*\hday+.5\hday) {\d};
        \foreach \g in {1,...,\numberofgroups}
          \node[etiquette groupe/.try] at (-.5\wgroupelabel,\dn*\hday+\g*\hgroupe-.5\hgroupe) {G\g};
      \fi
    }
    % mettre les heures (étiquettes horizontales et lignes verticales)
    \foreach[count=\hourlabel from \startinghour] \h in {1,...,\hoursperday}{
      \ifnum\h > 1\relax
        \draw[draw=black!49,ligne heure/.try] [xshift=(\h-1)*\hour]
          (0,-\hhouroffset) node[above,font=\sffamily,etiquette heure/.try]{\NoAutoSpacing\hourlabel:00}
            -- (0,\hplanning);
      \fi
        \draw[draw=black!35,very thin,ligne demi-heure/.try] [xshift=(\h-.5)*\hour]
          (0,-\hhouroffset/2) node[scale=.7,above,font=\sffamily,etiquette heure/.try,etiquette demi-heure/.try]{\NoAutoSpacing\hourlabel:30}
            -- (0,\hplanning);
    }
}{
  \end{tikzpicture}
}

% l'environnement module qui permet de définir le numéro et le nom du module
% le compteur du module est utilisé pour lui attribuer une couleur
\newcounter{module}\setcounter{module}{0}
\newenvironment{module}[2]{
  \xdef\numeromodule{#1}
  \xdef\titremodule{#2}
  \stepcounter{module}
  \begin{scope}%
}{\end{scope}}
