<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\CMS\Document;

\defined('_JEXEC') or die;

use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use Psr\Link\EvolvableLinkProviderInterface;

/**
 * Joomla! Preload Manager
 *
 * @since  4.0.0
 */
class PreloadManager implements PreloadManagerInterface
{
	/**
	 * The link provider
	 *
	 * @var    EvolvableLinkProviderInterface
	 * @since  4.0.0
	 */
	protected $linkProvider;

	/**
	 * PreloadManager constructor
	 *
	 * @param   EvolvableLinkProviderInterface  $linkProvider  The link provider
	 *
	 * @since   4.0.0
	 */
	public function __construct(EvolvableLinkProviderInterface $linkProvider = null)
	{
		$this->linkProvider = $linkProvider ?: new GenericLinkProvider;
	}

	/**
	 * Get the link provider
	 *
	 * @return  EvolvableLinkProviderInterface
	 *
	 * @since   4.0.0
	 */
	public function getLinkProvider(): EvolvableLinkProviderInterface
	{
		return $this->linkProvider;
	}

	/**
	 * Set the link provider
	 *
	 * @param   EvolvableLinkProviderInterface  $linkProvider  The link provider
	 *
	 * @return  $this
	 *
	 * @since   4.0.0
	 */
	public function setLinkProvider(EvolvableLinkProviderInterface $linkProvider)
	{
		$this->linkProvider = $linkProvider;

		return $this;
	}

	/**
	 * Preloads a resource.
	 *
	 * @param   string  $uri         A public path
	 * @param   array   $attributes  The attributes of this link (e.g. "array('as' => true)", "array('crossorigin' => 'use-credentials')")
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function preload(string $uri, array $attributes = [])
	{
		$this->link($uri, 'preload', $attributes);
	}

	/**
	 * Resolves a resource origin as early as possible.
	 *
	 * @param   string  $uri         A public path
	 * @param   array   $attributes  The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)")
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function dnsPrefetch(string $uri, array $attributes = [])
	{
		$this->link($uri, 'dns-prefetch', $attributes);
	}

	/**
	 * Initiates an early connection to a resource (DNS resolution, TCP handshake, TLS negotiation).
	 *
	 * @param   string  $uri         A public path
	 * @param   array   $attributes  The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)")
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function preconnect(string $uri, array $attributes = [])
	{
		$this->link($uri, 'preconnect', $attributes);
	}

	/**
	 * Indicates to the client that it should prefetch this resource.
	 *
	 * @param   string  $uri         A public path
	 * @param   array   $attributes  The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)")
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function prefetch(string $uri, array $attributes = [])
	{
		$this->link($uri, 'prefetch', $attributes);
	}

	/**
	 * Indicates to the client that it should prerender this resource.
	 *
	 * @param   string  $uri         A public path
	 * @param   array   $attributes  The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)")
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function prerender(string $uri, array $attributes = [])
	{
		$this->link($uri, 'prerender', $attributes);
	}

	/**
	 * Adds a "Link" HTTP header.
	 *
	 * @param   string  $uri         The relation URI
	 * @param   string  $rel         The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch")
	 * @param   array   $attributes  The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)")
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	private function link(string $uri, string $rel, array $attributes = [])
	{
		$link = new Link($rel, $uri);

		foreach ($attributes as $key => $value)
		{
			$link = $link->withAttribute($key, $value);
		}

		$this->setLinkProvider($this->getLinkProvider()->withLink($link));
	}
}
