<?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\HTML;

\defined('JPATH_PLATFORM') or die;

/**
 * Service registry for JHtml services
 *
 * @since  4.0.0
 */
final class Registry
{
	/**
	 * Mapping array of the core CMS JHtml helpers
	 *
	 * @var    array
	 * @since  4.0.0
	 */
	private $serviceMap = [
		'access'          => Helpers\Access::class,
		'actionsdropdown' => Helpers\ActionsDropdown::class,
		'adminlanguage'   => Helpers\AdminLanguage::class,
		'behavior'        => Helpers\Behavior::class,
		'bootstrap'       => Helpers\Bootstrap::class,
		'category'        => Helpers\Category::class,
		'content'         => Helpers\Content::class,
		'contentlanguage' => Helpers\ContentLanguage::class,
		'date'            => Helpers\Date::class,
		'debug'           => Helpers\Debug::class,
		'draggablelist'   => Helpers\DraggableList::class,
		'dropdown'        => Helpers\Dropdown::class,
		'email'           => Helpers\Email::class,
		'form'            => Helpers\Form::class,
		'formbehavior'    => Helpers\FormBehavior::class,
		'grid'            => Helpers\Grid::class,
		'icons'           => Helpers\Icons::class,
		'jgrid'           => Helpers\JGrid::class,
		'jquery'          => Helpers\Jquery::class,
		'links'           => Helpers\Links::class,
		'list'            => Helpers\ListHelper::class,
		'menu'            => Helpers\Menu::class,
		'number'          => Helpers\Number::class,
		'searchtools'     => Helpers\SearchTools::class,
		'select'          => Helpers\Select::class,
		'sidebar'         => Helpers\Sidebar::class,
		'sortablelist'    => Helpers\SortableList::class,
		'string'          => Helpers\StringHelper::class,
		'tag'             => Helpers\Tag::class,
		'tel'             => Helpers\Telephone::class,
		'uitab'           => Helpers\UiTab::class,
		'user'            => Helpers\User::class,
		'workflowstage'   => Helpers\WorkflowStage::class,
	];

	/**
	 * Get the service for a given key
	 *
	 * @param   string  $key  The service key to look up
	 *
	 * @return  string|object
	 *
	 * @since   4.0.0
	 */
	public function getService(string $key)
	{
		if (!$this->hasService($key))
		{
			throw new \InvalidArgumentException("The '$key' service key is not registered.");
		}

		return $this->serviceMap[$key];
	}

	/**
	 * Check if the registry has a service for the given key
	 *
	 * @param   string  $key  The service key to look up
	 *
	 * @return  boolean
	 *
	 * @since   4.0.0
	 */
	public function hasService(string $key): bool
	{
		return isset($this->serviceMap[$key]);
	}

	/**
	 * Register a service
	 *
	 * @param   string         $key      The service key to be registered
	 * @param   string|object  $handler  The handler for the service as either a PHP class name or class object
	 * @param   boolean        $replace  Flag indicating the service key may replace an existing definition
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function register(string $key, $handler, bool $replace = false)
	{
		// If the key exists already and we aren't instructed to replace existing services, bail early
		if (isset($this->serviceMap[$key]) && !$replace)
		{
			throw new \RuntimeException("The '$key' service key is already registered.");
		}

		// If the handler is a string, it must be a class that exists
		if (\is_string($handler) && !class_exists($handler))
		{
			throw new \RuntimeException("The '$handler' class for service key '$key' does not exist.");
		}

		// Otherwise the handler must be a class object
		if (!\is_string($handler) && !\is_object($handler))
		{
			throw new \RuntimeException(
				sprintf(
					'The handler for service key %1$s must be a PHP class name or class object, a %2$s was given.',
					$key,
					\gettype($handler)
				)
			);
		}

		$this->serviceMap[$key] = $handler;
	}
}
