<?php
declare(strict_types = 1);

/**
 * Gestion du diaporama.
 *
 * @license https://www.gnu.org/licenses/gpl-3.0.html
 * @link https://www.igalerie.org/
 */
class AjaxDiaporama extends Ajax
{
	/**
	 * Informations utiles de la catégorie.
	 *
	 * @var array
	 */
	private static $_categoryInfos = [];

	/**
	 * Nombre de fichiers de la série.
	 *
	 * @var int
	 */
	private static $_count = 0;

	/**
	 * Paramètres de la requête SQL.
	 *
	 * @var array
	 */
	private static $_params = [];

	/**
	 * Clause FROM de la requête SQL.
	 *
	 * @var string
	 */
	private static $_sqlFrom = '';

	/**
	 * Clause ORDER BY de la requête SQL.
	 *
	 * @var string
	 */
	private static $_sqlOrderBy = '';

	/**
	 * Clause WHERE de la requête SQL.
	 *
	 * @var string
	 */
	private static $_sqlWhere = '';

	/**
	 * Les votes sont-ils activés et autorisés pour l'utilisateur ?
	 *
	 * @var bool
	 */
	private static $_votes = FALSE;



	/**
	 * Récupération des données pour le diaporama.
	 *
	 * @return void
	 */
	public static function getData(): void
	{
		if (!Config::$params['diaporama'] || !is_array($_POST['key'] ?? '')
		|| !isset($_POST['q']) || !isset($_POST['get_item']) || !isset($_POST['get_carousel'])
		|| !Utility::checkPost('carousel_position', '`^\d{1,12}$`')
		|| !Utility::checkPost('item_position', '`^\d{1,12}$`')
		|| !Utility::checkPost('item_limit', '`^\d{1,2}$`')
		|| !Utility::checkPost('thumb_size', '`^\d{1,4}(?:x\d{1,4})?$`'))
		{
			self::_forbidden();
		}

		// Vérification de la clé de sécurité.
		foreach ($_POST['key'] as &$key)
		{
			if (!preg_match('`^[a-f0-9]{32}$`', (string) $key))
			{
				self::_forbidden();
			}
		}
		if (!in_array(md5($_POST['thumb_size'] . '|' . CONF_ACCESS_KEY), $_POST['key']))
		{
			self::_forbidden();
		}

		// Requête.
		$_GET['q'] = $_POST['q'] ?? '';
		self::_request();

		// Filtres.
		App::filtersGET();
		$filter = $_GET['filter'] ?? '';

		// Informations de la catégorie.
		self::_getCategoryInfos();

		// Permission de voter.
		self::$_votes = Config::$params['votes']
			&& ((Config::$params['users'] && Auth::$groupPerms['votes'])
			 || !Config::$params['users']);

		// Clause WHERE : filtre.
		$sql_where = [];
		if (isset($_GET['filter']))
		{
			$search_where = function() use (&$filter)
			{
				$search = new Search();
				if ($search->sql($_GET['search']) && !isset($search->options['type']))
				{
					if (!empty($search->options['favorites']))
					{
						$filter = 'user-favorites';
					}
					return $search->sql();
				}
				return FALSE;
			};
			if (($_GET['filter'] == 'search' && $where = $search_where())
			|| $where = SQL::itemsWhere($_GET['filter'], $_GET['filter_value']))
			{
				if (substr($_GET['filter'], 0, 4) == 'user'
				&& Config::$params['users'] && !Auth::$groupPerms['members_profile'])
				{
					self::_forbidden();
				}
				$sql_where[] = $where['sql'];
				self::$_params = $where['params'];
			}
			else
			{
				self::_forbidden();
			}
		}

		// Clause WHERE : catégorie.
		if (self::$_categoryInfos['cat_id'] > 1)
		{
			$sql_where[] = 'item_path LIKE :path';
			self::$_params['path'] = DB::likeEscape(self::$_categoryInfos['cat_path']) . '/%';
		}

		// Clause FROM.
		self::$_sqlFrom = '{items} AS i '
			. SQL::itemsFrom($filter)
			. ' LEFT JOIN {categories} AS cat ON i.cat_id = cat.cat_id';

		// Clause ORDER BY.
		$sql_order_by = Config::$params['items_sql_order_by'];
		if (!$filter = $_GET['filter'] ?? '')
		{
			$sql_order_by = self::$_categoryInfos['cat_orderby'] ?? $sql_order_by;
		}
		self::$_sqlOrderBy = SQL::itemsOrderBy($filter, $sql_order_by, self::$_categoryInfos);

		// Clause WHERE.
		$sql_where[] = 'item_status = "1"';
		self::$_sqlWhere = implode(' AND ', $sql_where);

		// Nombre de fichiers dans la série.
		$sql = 'SELECT COUNT(*)
				  FROM ' . self::$_sqlFrom . '
				 WHERE ' . self::$_sqlWhere . '
				   AND ' . SQL::catPerms() . '
				   AND ' . SQL::catPassword();
		DB::params(self::$_params);
		if (!DB::execute($sql))
		{
			self::_error();
		}
		self::$_count = DB::fetchVal();

		// Carrousel.
		if ($_POST['get_carousel'])
		{
			$carousel = self::_getCarousel();
		}

		// Fichiers.
		if ($_POST['get_item'])
		{
			$items = self::_getAllItems();

			// Nombre de vues.
			if ((!isset($_GET['filter']) || $_GET['filter'] != 'views')
			&& isset($items[$_POST['item_position']])
			&& Item::hit((int) $items[$_POST['item_position']]['id']) === 1)
			{
				$views = (int) $items[$_POST['item_position']]['stats']['views']['value'] + 1;
				$items[$_POST['item_position']]['stats']['views']['short']
					= L10N::formatShortNumber($views);
				$items[$_POST['item_position']]['stats']['views']['value']
					= $views;
			}
		}

		$data =
		[
			'status' => 'success',
			'carousel' => $carousel ?? [],
			'count' => (int) self::$_count,
			'items' => $items ?? []
		];

		// Paramètres du diaporama.
		if (!empty($_POST['start']))
		{
			$data['l10n'] = self::_getL10N();
			$data['params'] =
			[
				'favorites' => Auth::$connected
					&& Config::$params['favorites']
					&& Config::$params['users'],
				'geolocation' => (bool) Config::$params['geolocation'],
				'selection' => Selection::isActivated(),
				'tags' => (bool) Config::$params['tags'],
				'video_loop' => (bool) Config::$params['video_loop'],
				'video_muted' => (bool) Config::$params['video_muted'],
				'votes' => self::$_votes
			];
			if (Config::$params['geolocation'])
			{
				$default_layer = Config::$params['geolocation_default_layer'];
				if (!in_array($default_layer, ['map', 'satellite']))
				{
					$default_layer = 'map';
				}
				$default_zoom = (int) Config::$params['geolocation_default_zoom'];
				if ($default_zoom < 1 || $default_zoom > 18)
				{
					$default_zoom = 12;
				}
				$data['params']['geolocation_layer'] = $default_layer;
				$data['params']['geolocation_zoom'] = $default_zoom;
			}
		}

		self::_printResponse($data, FALSE);
	}



	/**
	 * Récupération des informations utiles de tous les fichiers demandés.
	 *
	 * @return array
	 */
	private static function _getAllItems(): array
	{
		// Récupération du fichier actuel et des fichiers précédents et suivants.
		$sql_limit = self::_sqlLimit((int) $_POST['item_position'], (int) $_POST['item_limit']);
		$items = self::_getItems($sql_limit);

		$all_items = [];

		$sql_limit = explode(',', $sql_limit);
		if (is_array($items))
		{
			$n = $sql_limit[0] + 1;
			foreach ($items as &$i)
			{
				$all_items[$n] = $i;
				$n++;
			}
		}

		// Récupération des premiers fichiers de la série.
		if ($sql_limit[0] != '0')
		{
			$items = self::_getItems("0," . $_POST['item_limit']);

			$n = 1;
			foreach ($items as &$i)
			{
				$all_items[$n] = $i;
				$n++;
			}
		}

		// Récupération des derniers fichiers de la série.
		if (($sql_limit[0] + $sql_limit[1]) < self::$_count)
		{
			$limit = $_POST['item_limit'];
			$items = self::_getItems((self::$_count - $limit) . "," . $limit);

			$n = self::$_count - $limit + 1;
			foreach ($items as &$i)
			{
				$all_items[$n] = $i;
				$n++;
			}
		}

		// Favoris, votes, sélection.
		Item::userFavorites($all_items);
		Item::userRating($all_items);
		Item::userSelection($all_items);

		// Formatage des données.
		require_once(__DIR__ . '/GalleryItems.class.php');
		$formated = [];
		$inames =
		[
			'date_created_link', 'date_created_text', 'date_published_link',
			'date_published_text', 'description', 'duration_text', 'exif', 'geolocation_coords',
			'geolocation_lat', 'geolocation_long', 'height', 'id', 'in_selection', 'iptc',
			'is_image', 'is_video', 'link', 'owner', 'source', 'stats', 'title', 'type_mime',
			'type_text', 'url', 'user', 'width', 'xmp'
		];
		foreach ($all_items as $k => &$i)
		{
			foreach ($formated_infos = GalleryItems::getFormatedInfos($i) as $fk => &$f)
			{
				if (in_array($fk, $inames))
				{
					$formated[$k][$fk] = $f;
				}
				if ($fk == 'filename' && $formated_infos['user']['edit'])
				{
					$formated[$k][$fk] = $f;
				}
			}

			// Dimensions redimensionnées.
			$formated[$k]['width_resized'] = (int) $i['item_width'];
			$formated[$k]['height_resized'] = (int) $i['item_height'];
			if ($formated[$k]['is_video'])
			{
				if ($formated[$k]['width_resized'] && $formated[$k]['height_resized'])
				{
					$min_width = 800;
					$min_height = 600;
					$resized =
					[
						'w' => $formated[$k]['width_resized'],
						'h' => $formated[$k]['height_resized']
					];

					$ratio_width = $formated[$k]['width_resized'] / $min_width;
					$ratio_height = $formated[$k]['height_resized'] / $min_height;

					if (($formated[$k]['width_resized'] < $min_width)
					&& ($ratio_width >= $ratio_height))
					{
						$resized['w'] = $min_width;
						$resized['h'] = round($formated[$k]['height_resized'] / $ratio_width);
					}
					else if (($formated[$k]['height_resized'] < $min_height)
					&& ($ratio_height >= $ratio_width))
					{
						$resized['w'] = round($formated[$k]['width_resized'] / $ratio_height);
						$resized['h'] = $min_height;
					}

					$formated[$k]['width'] = $formated[$k]['width_resized'] = $resized['w'];
					$formated[$k]['height'] = $formated[$k]['height_resized'] = $resized['h'];
				}
				else
				{
					$formated[$k]['width_resized'] = 800;
					$formated[$k]['height_resized'] = 600;
				}
			}
			else if (preg_match('`resize\.php.+&size=(\d{1,5})x(\d{1,5})`',
			$formated[$k]['source'], $m))
			{
				$resized = Image::getResizedSize((int) $i['item_width'],
					(int) $i['item_height'], (int) $m[1], (int) $m[2]);
				$formated[$k]['width_resized'] = $resized['w'];
				$formated[$k]['height_resized'] = $resized['h'];
			}

			// Position du fichier dans la série.
			$text = $formated[$k]['is_video'] ? __('Vidéo %s/%s') : __('Photo %s/%s');
			$position_text = sprintf($text, $k, self::$_count);
			$formated[$k]['position_text'] = $position_text;
			$formated[$k]['position'] = sprintf('%s/%s', $k, self::$_count);

			// Signature pour identifier le fichier afin de mettre à jour
			// le diaporama en cas de changement du fichier ou de la série.
			$formated[$k]['md5'] = md5(implode('|', [$position_text, $i['item_id'],
				$formated[$k]['height_resized'], $formated[$k]['width_resized']]));

			// Poster.
			if ($formated[$k]['is_video'])
			{
				$formated[$k]['poster'] = App::getThumbSource('item', $i,
					(string) THEME_CONFIG['diaporama_video_poster_size'], '', TRUE);
			}

			// Téléchargement.
			if (GalleryItems::itemDownload($i))
			{
				$formated[$k]['download'] = App::getFileSource($i['item_path'], TRUE);
			}

			// Statistiques.
			unset($formated[$k]['stats']['filter']);

			// Métadonnées.
			$metadata = GalleryItems::getMetadata($i);
			$formated[$k]['exif'] = &$metadata['exif'];
			$formated[$k]['iptc'] = &$metadata['iptc'];
			$formated[$k]['xmp'] = &$metadata['xmp'];

			// Tags.
			$formated[$k]['tags'] = GalleryItems::getTags((int) $i['item_id']);

			// Fil d'Ariane.
			Template::breadcrumb($i, TRUE, 'category', FALSE);
			$formated[$k]['breadcrumb'] = [];
			foreach (Template::$data['breadcrumb'] as &$object)
			{
				$formated[$k]['breadcrumb'][] =
				[
					'url' => $object['url'],
					'name' => $object['name']
				];
			}
			unset($formated[$k]['breadcrumb'][0]);
			unset(Template::$data['breadcrumb']);

			// Géolocalisation.
			if (!Config::$params['geolocation'])
			{
				unset($formated[$k]['geolocation_coords']);
				unset($formated[$k]['geolocation_lat']);
				unset($formated[$k]['geolocation_long']);
			}

			// Utilisateur.
			if (Config::$params['users'])
			{
				if (!Auth::$groupPerms['members_profile'])
				{
					unset($formated[$k]['owner']['link']);
				}
			}
			else
			{
				unset($formated[$k]['owner']);
			}

			// Description.
			if (Config::$params['items_description_model'])
			{
				$desc = Description::model('item', $i);
				$formated[$k]['description_formated'] = Utility::isEmpty($desc) ? NULL : $desc;
			}
			elseif (is_string($formated[$k]['description']))
			{
				$desc = $formated[$k]['description'];
				HTML::specialchars($desc);
				$formated[$k]['description_formated'] = Template::formatText($desc);
			}

			// Votable ?
			$formated[$k]['votable'] = self::$_votes
				&& $i['parent_votable'] && $i['item_votable'];
		}

		return $formated;
	}

	/**
	 * Récupération des informations utiles des fichiers du carrousel.
	 *
	 * @return array
	 */
	private static function _getCarousel(): array
	{
		// Clause LIMIT.
		$sql_limit = self::_sqlLimit((int) $_POST['carousel_position'], 60);

		// On récupère les informations utiles des fichiers.
		$sql = "SELECT i.item_id,
					   i.item_type,
					   i.item_path,
					   i.item_url,
					   i.item_width,
					   i.item_height,
					   i.item_orientation,
					   i.item_duration,
					   i.item_name,
					   i.item_tb_params,
					   i.item_adddt
				  FROM " . self::$_sqlFrom . "
				 WHERE " . self::$_sqlWhere . "
				   AND " . SQL::catPerms() . "
				   AND " . SQL::catPassword() . "
			  ORDER BY " . self::$_sqlOrderBy . "
			     LIMIT $sql_limit";
		DB::params(self::$_params);
		if (!DB::execute($sql))
		{
			self::_error();
		}

		$formated = [];
		$position = (int) (preg_replace('`,\d+$`', '', $sql_limit)) + 1;
		foreach (DB::fetchAll() as &$i)
		{
			$formated[$position] =
			[
				'duration' => $i['item_duration']
					? App::formatDuration($i['item_duration'])
					: '00:00',
				'is_video' => Item::isVideo($i['item_type']),
				'source' => App::getThumbSource('item', $i, (string) $_POST['thumb_size']),
				'title' => $i['item_name']
			];
			$position++;
		}

		return $formated;
	}

	/**
	 * Récupération des informations utiles de la catégorie courante.
	 *
	 * @return void
	 */
	private static function _getCategoryInfos(): void
	{
		$sql = 'SELECT cat_id,
					   cat_path,
					   cat_name,
					   cat_url,
					   cat_filemtime,
					   cat_orderby
				  FROM {categories} AS cat
				 WHERE ' . SQL::catPerms() . '
				   AND ' . SQL::catPassword() . '
				   AND cat_id = :cat_id
				   AND cat_status = "1"
				   AND cat_filemtime IS ';
		$sql .= $_GET['section'] == 'category' ? 'NULL ' : 'NOT NULL ';
		DB::params(['cat_id' => (int) $_GET['category_id']]);
		if (!DB::execute($sql))
		{
			self::_error();
		}
		if (!isset((self::$_categoryInfos = DB::fetchRow())['cat_id']))
		{
			self::_forbidden();
		}
	}

	/**
	 * Récupération de plusieurs fichiers au sein de la série.
	 *
	 * @param string $sql_limit
	 *
	 * @return array
	 */
	private static function _getItems(string $sql_limit): array
	{
		// On récupère les informations utiles des fichiers.
		$sql_cameras = '';
		if (empty($_GET['filter']) || substr($_GET['filter'], 0, 6) != 'camera')
		{
			$sql_cameras = '
			 LEFT JOIN {cameras_items} AS cam_i
				    ON cam_i.item_id = i.item_id
		     LEFT JOIN {cameras_models} AS cam_m
				    ON cam_m.camera_model_id = cam_i.camera_model_id
		     LEFT JOIN {cameras_brands} AS cam_b
				    ON cam_b.camera_brand_id = cam_m.camera_brand_id';
		}
		$sql = "SELECT i.*,
					   cat.cat_id,
					   cat.cat_name,
					   cat.cat_url,
					   cat.cat_watermark,
					   cat.cat_parents,
					   'album' AS cat_type,
					   u.user_avatar,
					   u.user_login,
					   u.user_nickname,
					   u.user_status,
					   cam_m.*,
					   cam_b.*
				  FROM " . self::$_sqlFrom . "
			 LEFT JOIN {users} AS u
					ON i.user_id = u.user_id
					   $sql_cameras
				 WHERE " . self::$_sqlWhere . "
				   AND " . SQL::catPerms() . "
				   AND " . SQL::catPassword() . "
			  ORDER BY " . self::$_sqlOrderBy . "
			     LIMIT $sql_limit";
		DB::params(self::$_params);
		if (!DB::execute($sql))
		{
			self::_error();
		}
		$items = DB::fetchAll();

		// Réglages.
		foreach ($items as &$i)
		{
			$cat_parents = $i['cat_parents'];
			$i['cat_parents'] .= $i['cat_id'] . Parents::SEPARATOR;
			$i = [$i];
			Parents::settings($i);
			$i = $i[0];
			$i['cat_parents'] = $cat_parents;
		}

		return $items;
	}

	/**
	 * Texte localisé de l'interface du diaporama.
	 *
	 * @return array
	 */
	private static function _getL10N()
	{
		return
		[
			'at_launch' => __('Au lancement'),
			'avatar' => __('Avatar'),
			'auto' => __('Lecture automatique'),
			'auto_duration_mobile' => __('Durée d\'affichage des fichiers :'),
			'auto_loop' => __('Lecture en boucle'),
			'auto_start' => __('Démarrage au lancement'),
			'carousel_option' => __('Carrousel'),
			'close' => __('Fermer'),
			'control_bars' => __('Barres de contrôle en permanence'),
			'date_user_published' => __('Publiée le %s par %s'),
			'delete' => __('Supprimer'),
			'desc' => __('Description'),
			'display' => __('Affichage'),
			'download' => __('Télécharger'),
			'edit' => __('Édition'),
			'edit_desc' => __('Description :'),
			'edit_filename' => __('Nom de fichier :'),
			'edit_tags' => __('Tags (séparés par une virgule) :'),
			'edit_title' => __('Titre :'),
			'effects' =>
			[
				'none' => __('Aucun'),
				'fade' => __('Fondu'),
				'slideX' => __('Défilement (horizontal)'),
				'slideY' => __('Défilement (vertical)'),
				'slideXLeft' => __('Diapositive (horizontal)'),
				'slideYBottom' => __('Diapositive (vertical)'),
				'curtainX' => __('Rideau (horizontal)'),
				'curtainY' => __('Rideau (vertical)'),
				'puff' => __('Souffle'),
				'zoom' => __('Zoom'),
				'random' => __('Aléatoire')
			],
			'exif' => __('Informations EXIF'),
			'favorites_add' => __('Ajouter à mes favoris'),
			'favorites_remove' => __('Retirer de mes favoris'),
			'full_screen' => __('Mode plein écran'),
			'geolocation' => __('Géolocalisation'),
			'geolocation_map' => __('Plan'),
			'geolocation_none' => __('Aucune information disponible.'),
			'geolocation_satellite' => __('Satellite'),
			'informations' => __('Informations'),
			'iptc' => __('Informations IPTC'),
			'keyboard_keys' =>
			[
				'decrease_display_time' => __('Diminue la durée d\'affichage'),
				'description' => __('Description sur les photos'),
				'carousel' => __('Affiche ou masque le carrousel'),
				'carousel_next' => __('Avance le carrousel'),
				'carousel_next_key' => __('&#x21DF;'),
				'carousel_prev' => __('Recule le carrousel'),
				'carousel_prev_key' => __('&#x21DE;'),
				'close' => __('Ferme le diaporama'),
				'close_key' => __('Échap'),
				'control' => __('Affiche ou masque les barres de contrôle'),
				'favorites' => __('Ajoute la photo aux favoris'),
				'first_photo' => __('Première photo'),
				'first_photo_key' => __('&#x2196;'),
				'geolocation' => __('Panneau de géolocalisation'),
				'increase_display_time' => __('Augmente la durée d\'affichage'),
				'informations' => __('Panneau d\'informations'),
				'keyboard' => __('Contrôle au clavier'),
				'last_photo' => __('Dernière photo'),
				'last_photo_key' => __('Fin'),
				'next_photo' => __('Photo suivante'),
				'previous_photo' => __('Photo précédente'),
				'selection' => __('Ajoute la photo à la sélection'),
				'size' => __('Taille réelle ou redimensionnée'),
				'space' => __('Démarre ou arrête la lecture'),
				'space_key' => __('Espace'),
				'title' => __('Titre sur les photos')
			],
			'keyboard_control' => __('Contrôle au clavier'),
			'keyboard_tip' => __('Appuyez sur %s pour afficher la liste des touches'
				. ' permettant de contrôler le diaporama au clavier.'),
			'navigation' => __('Navigation'),
			'other_functions' => __('Autres fonctions'),
			'over_image_title' => __('Titre sur les photos'),
			'over_image_description' => __('Description sur les photos'),
			'pixels' => __('pixels'),
			'properties' => __('Propriétés'),
			'properties_date_created' => __('Date de création :'),
			'properties_duration' => __('Durée :'),
			'properties_filesize' => __('Poids :'),
			'properties_filetype' => __('Type de fichier :'),
			'properties_size' => __('Dimensions :'),
			'rating_disabled' => __('L\'ajout de note est désactivé pour ce fichier.'),
			'rating_user' => __('Votre note :'),
			'save' => __('Enregistrer'),
			'selection_add' => __('Ajouter à la sélection'),
			'selection_remove' => __('Retirer de la sélection'),
			'settings' => __('Options'),
			'show_informations' => __('Ouvrir le panneau d\'informations'),
			'special' => __('Touches spéciales'),
			'stats' => __('Statistiques'),
			'stats_views' => __('Nombre de vues :'),
			'stats_favorites' => __('Nombre de favoris :'),
			'stats_comments' => __('Nombre de commentaires :'),
			'stats_votes' => __('Nombre de votes :'),
			'stats_rating' => __('Note moyenne :'),
			'tags' => __('Tags'),
			'transition' => __('Transition entre les photos'),
			'transitions_duration' => __('Durée de transition (en millisecondes) :'),
			'transitions_effect' => __('Effet de transition :'),
			'user' => __('Utilisateur'),
			'votes' => __('Ajouter une note'),
			'votes_number' => __('%s vote'),
			'votes_number_multiple' => __('%s votes'),
			'xmp' => __('Informations XMP')
		];
	}

	/**
	 * Gestion des paramètres GET.
	 *
	 * @return void
	 */
	private static function _request(): void
	{
		// Configuration de base.
		$request =
		[
			'{category}/{id}',
			'{category}/{id}/camera-(brand|model)/{id}',
			'{category}/{id}/date-(created|published)/{date-flex}',
			'{category}/{id}/images',
			'{category}/{id}/items',
			'{category}/{id}/selection',
			'{category}/{id}/videos',
			'{category}/{id}/views'
		];

		// Commentaires.
		if (Config::$params['comments'])
		{
			$request[] = '{category}/{id}/comments';
		}

		// Fichiers récents.
		if (Config::$params['items_recent'])
		{
			$request[] = '{category}/{id}/recent-(images|items|videos)';
		}

		// Moteur de recherche.
		if (Config::$params['search'])
		{
			$request[] = '{category}/{id}/search/{search}';
		}

		// Tags.
		if (Config::$params['tags'])
		{
			$request[] = '{category}/{id}/tag/{id}';
			$request[] = '{category}/{id}/tags/{ids}';
		}

		// Utilisateurs.
		if (Config::$params['users'])
		{
			$request[] = '{category}/{id}/user-(images|items|videos)/{id}';

			// Favoris.
			if (Config::$params['favorites'])
			{
				$request[] = '{category}/{id}/favorites';
				$request[] = '{category}/{id}/user-favorites/{id}';
			}
		}

		// Votes.
		if (Config::$params['votes'])
		{
			$request[] = '{category}/{id}/votes';
		}

		// Analyse de la requête.
		App::request($request);

		// Sélection.
		if (($_GET['section_2'] ?? '') == 'selection' && !Selection::isActivated())
		{
			self::_forbidden();
		}

		// Paramètres GET par défaut.
		if (!isset($_GET['section']))
		{
			self::_forbidden();
		}
		if (isset($_GET['album_id']))
		{
			$_GET['category_id'] = $_GET['album_id'];
		}
		if (!isset($_GET['category_id']))
		{
			$_GET['category_id'] = 1;
		}
	}

	/**
	 * Clause LIMIT pour la récupération de fichiers autour du fichier actuel.
	 *
	 * @param int $position
	 * @param int $max
	 *
	 * @return string
	 */
	private static function _sqlLimit(int $position, int $max): string
	{
		$start = $position - $max - 1;
		$items_count = ($max * 2) + 1;

		if ($start < 0)
		{
			$items_count -= -$start;
			$start = 0;
		}

		return "$start,$items_count";
	}
}
?>