<?php
declare(strict_types = 1);

/**
 * Fabrication de flux RSS.
 *
 * @license https://www.gnu.org/licenses/gpl-3.0.html
 * @link https://www.igalerie.org/
 */
class RSS
{
	/**
	 * Langue du flux RSS.
	 *
	 * @var string
	 */
	private $_lang;

	/**
	 * Informations utiles des objets (fichiers ou commentaires).
	 *
	 * @var array
	 */
	private $_objects = [];



	/**
	 * Initialisation.
	 *
	 * @return void
	 */
	public function __construct(string $lang)
	{
		// Connexion à la base de données.
		if (!DB::connect())
		{
			die('Impossible de se connecter à la base de données.'
				. '<br>Unable to connect to database.');
		}

		// Récupération de la configuration.
		Config::getDBParams();

		// Localisation.
		L10N::locale($lang);

		// Galerie fermée ou flux RSS désactivés ?
		if (Config::$params['gallery_closed'] || !Config::$params['rss'])
		{
			die(App::httpResponse(403));
		}

		// Authentification.
		if (!Auth::cookie() && Config::$params['users_only_members'])
		{
			die(App::httpResponse(403));
		}

		// Langue.
		$this->_lang = $lang;

		// Fichiers à activer ou désactiver.
		Item::pubexp();

		// Opérations quotidiennes.
		App::dailyUpdate();
	}

	/**
	 * Flux de catégorie (commentaires ou fichiers).
	 *
	 * @param int $id
	 *   Identifiant de la catégorie.
	 * @param string $type
	 *   Type de flux : 'comments' ou 'items'.
	 *
	 * @return void
	 */
	public function makeCategoryFeed(int $id, string $type): void
	{
		// Récupération des informations utiles de la catégorie.
		$sql = 'SELECT cat_path,
		               cat_name,
					   CASE WHEN cat_filemtime IS NULL
							THEN "category"
							ELSE "album"
							 END AS type,
					   cat_a_images,
					   cat_a_videos
			      FROM {categories} AS cat
				 WHERE cat_id = :cat_id
				   AND cat_status = "1"
				   AND ' . SQL::catPerms() . '
				   AND ' . SQL::catPassword();
		DB::params(['cat_id' => $id]);
		if (!DB::execute($sql))
		{
			die(App::httpResponse(500));
		}
		if (!$category = DB::fetchRow())
		{
			die(App::httpResponse(404));
		}

		DB::params(['cat_path' => $id == 1
			? '%'
			: DB::likeEscape($category['cat_path']) . '/%'
		]);

		// Récupération des derniers fichiers.
		if ($type == 'items')
		{
			$this->_getItems('', 'item_path LIKE :cat_path');
		}

		// Récupération des derniers commentaires.
		if ($type == 'comments')
		{
			$this->_getComments('item_path LIKE :cat_path');
		}

		$this->_print($type,
		[
			'id' => $id,
			'name' => $category['cat_name'],
			'images_count' => (int) $category['cat_a_images'],
			'videos_count' => (int) $category['cat_a_videos'],
			'type' => $category['type']
		]);
	}

	/**
	 * Flux de fichier (commentaires uniquement).
	 *
	 * @param int $id
	 *   Identifiant du fichier.
	 *
	 * @return void
	 */
	public function makeItemFeed(int $id): void
	{
		// Récupération des informations utiles du fichier.
		$sql = 'SELECT item_path,
		               item_name,
					   item_type
			      FROM {items} AS i
			 LEFT JOIN {categories} AS cat USING (cat_id)
				 WHERE item_id = :item_id
				   AND item_status = "1"
				   AND ' . SQL::catPerms() . '
				   AND ' . SQL::catPassword();
		DB::params(['item_id' => $id]);
		if (!DB::execute($sql))
		{
			die(App::httpResponse(500));
		}
		if (!$item = DB::fetchRow())
		{
			die(App::httpResponse(404));
		}

		DB::params(['item_id' => $id]);
		$this->_getComments('i.item_id = :item_id');

		$this->_print('comments',
		[
			'id' => $id,
			'name' => $item['item_name'],
			'type' => Item::isImage($item['item_type']) ? 'image' : 'video'
		]);
	}

	/**
	 * Flux de tag (fichiers uniquement).
	 *
	 * @param int $id
	 *   Identifiant du tag.
	 *
	 * @return void
	 */
	public function makeTagFeed(int $id): void
	{
		// Récupération des informations utiles du tag.
		if (!DB::execute('SELECT tag_name FROM {tags} WHERE tag_id = ?', $id))
		{
			die(App::httpResponse(500));
		}
		if (!$tag = DB::fetchRow())
		{
			die(App::httpResponse(404));
		}

		// Récupération des informations utiles de la galerie.
		if (!DB::execute('SELECT cat_a_images, cat_a_videos FROM {categories} WHERE cat_id = 1'))
		{
			die(App::httpResponse(500));
		}
		$tag = array_merge($tag, DB::fetchRow());

		DB::params(['tag_id' => $id]);
		$this->_getItems('LEFT JOIN {tags_items} AS ti USING (item_id)', 'tag_id = :tag_id');

		$this->_print('items', $tag);
	}



	/**
	 * Récupération des derniers commentaires.
	 *
	 * @param string $sql_where
	 *
	 * @return void
	 */
	private function _getComments(string $sql_where): void
	{
		if (Config::$params['users'] && !Auth::$groupPerms['comments_read'])
		{
			return;
		}
		$sql_limit = (int) Config::$params['rss_max_items'];
		$sql = "SELECT com.com_id,
					   com.com_crtdt,
					   com.com_message,
					   i.item_id,
					   i.item_name,
					   i.item_url,
					   u.user_id,
					   CASE WHEN u.user_id = 2
							THEN com_author
							ELSE user_login
							 END AS author
				  FROM {comments} AS com,
					   {items} AS i,
					   {categories} AS cat,
					   {users} AS u
				 WHERE com.item_id = i.item_id
				   AND com.user_id = u.user_id
				   AND cat.cat_id = i.cat_id
				   AND $sql_where
				   AND " . SQL::catPerms() . "
				   AND " . SQL::catPassword() . "
				   AND i.item_status = '1'
				   AND com.com_status = '1'
			  ORDER BY com_id DESC
				 LIMIT $sql_limit";
		if (!DB::execute($sql))
		{
			die(App::httpResponse(500));
		}
		$this->_objects = DB::fetchAll();
	}

	/**
	 * Récupération des derniers fichiers.
	 *
	 * @param string $sql_from
	 * @param string $sql_where
	 *
	 * @return void
	 */
	private function _getItems(string $sql_from, string $sql_where): void
	{
		$sql_limit = (int) Config::$params['rss_max_items'];
		$sql = "SELECT item_id,
					   item_name,
					   item_path,
					   item_url,
					   item_type,
					   item_width,
					   item_height,
					   item_orientation,
					   item_desc,
					   item_adddt,
					   item_pubdt,
					   cat_name
				  FROM {items} AS i
			 LEFT JOIN {categories} AS cat USING (cat_id)
					   $sql_from
				 WHERE $sql_where
				   AND " . SQL::catPerms() . "
				   AND " . SQL::catPassword() . "
				   AND item_status = '1'
			  ORDER BY item_id DESC
				 LIMIT $sql_limit";
		if (!DB::execute($sql))
		{
			die(App::httpResponse(500));
		}
		$this->_objects = DB::fetchAll();
	}

	/**
	 * Fabrique et imprime le flux RSS.
	 *
	 * @param string $type
	 *   Type de flux : 'comments' ou 'items'.
	 * @param array $object
	 *   Informations de l'objet.
	 *
	 * @return void
	 */
	private function _print(string $type, array $object): void
	{
		// Informations.
		$title = Config::$params['gallery_title'];
		$language = substr($this->_lang, 0, 2);
		$last_build_date = date(DATE_RSS);

		// Description du flux.
		switch ($object['type'] ?? '')
		{
			case 'album' :
				$object['name'] = sprintf(__('l\'album %s'), $object['name']);
				break;

			case 'category' :
				$object['name'] = ($object['id'] > 1)
					? sprintf(__('la catégorie %s'), $object['name'])
					: __('la galerie');
				break;

			case 'image' :
				$object['name'] = sprintf(__('la photo %s'), $object['name']);
				break;

			case 'video' :
				$object['name'] = sprintf(__('la vidéo %s'), $object['name']);
				break;
		}
		switch ($type)
		{
			case 'comments' :
				$description = sprintf(__('Derniers commentaires de %s'), $object['name']);
				$title .= ' - ' . __('commentaires');
				break;

			case 'items' :
				if (isset($object['tag_name']))
				{
					if ($object['cat_a_images'] && $object['cat_a_videos'])
					{
						$description = sprintf(
							__('Dernières photos et vidéos liées au tag %s'), $object['tag_name']
						);
						$title .= ' - ' . __('photos et vidéos');
					}
					else if ($object['cat_a_images'])
					{
						$description = sprintf(
							__('Dernières photos liées au tag %s'), $object['tag_name']
						);
						$title .= ' - ' . __('photos');
					}
					else if ($object['cat_a_videos'])
					{
						$description = sprintf(
							__('Dernières vidéos liées au tag %s'), $object['tag_name']
						);
						$title .= ' - ' . __('vidéos');
					}
				}
				else
				{
					if ($object['images_count'] && $object['videos_count'])
					{
						$description = sprintf(
							__('Dernières photos et vidéos de %s'), $object['name']
						);
						$title .= ' - ' . __('photos et vidéos');
					}
					else if ($object['images_count'])
					{
						$description = sprintf(__('Dernières photos de %s'), $object['name']);
						$title .= ' - ' . __('photos');
					}
					else if ($object['videos_count'])
					{
						$description = sprintf(__('Dernières vidéos de %s'), $object['name']);
						$title .= ' - ' . __('vidéos');
					}
				}
				break;
		}

		// On imprime le flux.
		header('Content-Type: text/xml; charset=' . CONF_CHARSET);
		echo '<?xml version="1.0" encoding="' . CONF_CHARSET . '"?>' . "\n";
		echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">' . "\n";
		echo '<channel>' . "\n";
		echo '<title>' . htmlspecialchars($title) . '</title>' . "\n";
		echo '<link>' . htmlspecialchars(GALLERY_HOST . CONF_GALLERY_PATH) . '/</link>' . "\n";
		echo '<atom:link href="' . htmlspecialchars(GALLERY_HOST . $_SERVER['REQUEST_URI'])
			. '" rel="self" type="application/rss+xml"/>' . "\n";
		echo '<description>' . htmlspecialchars($description ?? '') . '</description>' . "\n";
		echo '<language>' . htmlspecialchars($language) . '</language>' . "\n";
		echo '<generator>' . htmlspecialchars(System::APP_WEBSITE) . '</generator>' . "\n";
		echo '<lastBuildDate>' . htmlspecialchars($last_build_date) . '</lastBuildDate>' . "\n";

		switch ($type)
		{
			case 'comments' :
				self::_printComments();
				break;

			case 'items' :
				self::_printItems();
				break;
		}

		echo '</channel>' . "\n";
		echo '</rss>';
		die;
	}

	/**
	 * Imprime les éléments du flux RSS des commentaires.
	 *
	 * @return void
	 */
	private function _printComments(): void
	{
		foreach ($this->_objects as &$i)
		{
			// Titre.
			$title = htmlspecialchars($i['item_name'] . ' - ' . $i['author']);

			// Lien.
			$link = htmlspecialchars(GALLERY_HOST . App::getURL('item/' . $i['item_id']
				. '-' . $i['item_url'] . '#comment-' . $i['com_id']));

			// Date de publication.
			$pub_date = date(DATE_RSS, strtotime($i['com_crtdt']));

			// Description.
			$description = Template::formatComment(htmlspecialchars($i['com_message']));

			// guid.
			$guid = md5($link . '|' . $pub_date);

			// XML.
			echo "<item>\n";
			echo "<title>$title</title>\n";
			echo "<link>$link</link>\n";
			echo "<pubDate>$pub_date</pubDate>\n";
			echo "<description><![CDATA[$description]]></description>\n";
			echo "<guid isPermaLink=\"false\">md5:$guid</guid>\n";
			echo "</item>\n";
		}
	}

	/**
	 * Imprime les éléments du flux RSS des fichiers.
	 *
	 * @return void
	 */
	private function _printItems(): void
	{
		foreach ($this->_objects as &$i)
		{
			// Titre.
			$title = htmlspecialchars($i['item_name'] . ' - ' . $i['cat_name']);

			// Lien.
			$link = GALLERY_HOST . App::getURL('item/' . $i['item_id'] . '-' . $i['item_url']);

			// Date de publication.
			$pub_date = date(DATE_RSS, strtotime($i['item_pubdt']));

			// Description.
			$description = '';
			if (!CONF_THUMBS_SECURE)
			{
				// Vignette.
				$thumb = htmlspecialchars(App::getThumbSource('item', $i,
					CONF_THUMBS_SIZE_ITEM, (string) CONF_THUMBS_QUALITY));
				$filename = htmlspecialchars(basename($i['item_path']));
				$size = Image::getResizedSize((int) $i['item_width'], (int) $i['item_height'],
					(int) CONF_THUMBS_SIZE_ITEM, (int) CONF_THUMBS_SIZE_ITEM,
					(int) $i['item_orientation']);
				$description = '<a href="' . $link . '"><img alt="' . $filename
					. '" width="' . $size['w'] . '" height="' . $size['h']
					. '" src="' . GALLERY_HOST . $thumb . '"/></a>';
			}
			if (!Utility::isEmpty((string) $i['item_desc']))
			{
				if ($description)
				{
					$description .= '<br/><br/>';
				}
				$description .= nl2br(HTML::decode($i['item_desc']));
			}

			// guid.
			$guid = md5($link . '|' . $pub_date);

			// XML.
			echo "<item>\n";
			echo "<title>$title</title>\n";
			echo "<link>$link</link>\n";
			echo "<pubDate>$pub_date</pubDate>\n";
			echo "<description><![CDATA[$description]]></description>\n";
			echo "<guid isPermaLink=\"false\">md5:$guid</guid>\n";
			echo "</item>\n";
		}
	}
}
?>