<?php

/**
 * Part of the Joomla Framework DI Package.
 *
 * @copyright  Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

namespace _JchOptimizeVendor\Joomla\DI;

/**
 * Defines the representation of a resource.
 *
 * @since  2.0.0
 *
 * @internal
 */
final class ContainerResource
{
    /**
     * Defines the resource as non-shared.
     *
     * @const  integer
     *
     * @since  2.0.0
     */
    public const NO_SHARE = 0;

    /**
     * Defines the resource as shared.
     *
     * @const  integer
     *
     * @since  2.0.0
     */
    public const SHARE = 1;

    /**
     * Defines the resource as non-protected.
     *
     * @const  integer
     *
     * @since  2.0.0
     */
    public const NO_PROTECT = 0;

    /**
     * Defines the resource as protected.
     *
     * @const  integer
     *
     * @since  2.0.0
     */
    public const PROTECT = 2;

    /**
     * The container the resource is assigned to.
     *
     * @var Container
     *
     * @since  2.0.0
     */
    private $container;

    /**
     * The object instance for a shared object.
     *
     * @var mixed
     *
     * @since  2.0.0
     */
    private $instance;

    /**
     * The factory object.
     *
     * @var callable
     *
     * @since  2.0.0
     */
    private $factory;

    /**
     * Flag if the resource is shared.
     *
     * @var bool
     *
     * @since  2.0.0
     */
    private $shared = \false;

    /**
     * Flag if the resource is protected.
     *
     * @var bool
     *
     * @since  2.0.0
     */
    private $protected = \false;

    /**
     * Create a resource representation.
     *
     * @param Container $container The container
     * @param mixed     $value     The resource or its factory closure
     * @param int       $mode      Resource mode, defaults to Resource::NO_SHARE | Resource::NO_PROTECT
     *
     * @since   2.0.0
     */
    public function __construct(Container $container, $value, int $mode = 0)
    {
        $this->container = $container;
        $this->shared = ($mode & self::SHARE) === self::SHARE;
        $this->protected = ($mode & self::PROTECT) === self::PROTECT;
        if (\is_callable($value)) {
            $this->factory = $value;
        } else {
            if ($this->shared) {
                $this->instance = $value;
            }
            if (\is_object($value)) {
                $this->factory = function () use ($value) {
                    return clone $value;
                };
            } else {
                $this->factory = function () use ($value) {
                    return $value;
                };
            }
        }
    }

    /**
     * Check whether the resource is shared.
     *
     * @since   2.0.0
     */
    public function isShared(): bool
    {
        return $this->shared;
    }

    /**
     * Check whether the resource is protected.
     *
     * @since   2.0.0
     */
    public function isProtected(): bool
    {
        return $this->protected;
    }

    /**
     * Get an instance of the resource.
     *
     * If a factory was provided, the resource is created and - if it is a shared resource - cached internally.
     * If the resource was provided directly, that resource is returned.
     *
     * @return mixed
     *
     * @since   2.0.0
     */
    public function getInstance()
    {
        $callable = $this->factory;
        if ($this->isShared()) {
            if (null === $this->instance) {
                $this->instance = $callable($this->container);
            }

            return $this->instance;
        }

        return $callable($this->container);
    }

    /**
     * Get the factory.
     *
     * @since   2.0.0
     */
    public function getFactory(): callable
    {
        return $this->factory;
    }

    /**
     * Reset the resource.
     *
     * The instance cache is cleared, so that the next call to get() returns a new instance.
     * This has an effect on shared, non-protected resources only.
     *
     * @return bool True if the resource was reset, false otherwise
     *
     * @since   2.0.0
     */
    public function reset(): bool
    {
        if ($this->isShared() && !$this->isProtected()) {
            $this->instance = null;

            return \true;
        }

        return \false;
    }
}
