<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

if (!class_exists('ViisonCommon_Plugin_BootstrapV2')) {
    require_once('PluginBootstrapV2.php');
}

use Enlight\Event\SubscriberInterface;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util;

/**
 * Version 3 of the common plugin Bootstrap class. This version is based on ViisonCommon_Plugin_BootstrapV2
 * and provides an extra method for checking, whether this plugin is installed and active and ViisonCommon's
 * namespace is loaded.
 *
 * This class needs to be manually loaded in the respective plugin via:
 *
 * if (!class_exists('ViisonCommon_Plugin_BootstrapV3')) {
 *     require_once('ViisonCommon/PluginBootstrapV3.php');
 * }
 */
class ViisonCommon_Plugin_BootstrapV3 extends ViisonCommon_Plugin_BootstrapV2
{
    /**
     * @var array|null $namespaceLoadingCallbacks
     */
    protected static $namespaceLoadingCallbacks;

    /**
     * @var boolean $namespaceLoadingCallbacksCalled
     */
    protected static $namespaceLoadingCallbacksCalled = false;

    /**
     * @see ViisonCommon_Plugin_Bootstrap::registerViisonCommonNamespace()
     *
     * Saves the optional $callback in a static array before calling registerViisonCommonNamespace().
     * Finally, if the namespace was registered successfully, all saved callbacks are called exactly once.
     *
     * Usage: Call once in the plugin bootstrap's 'afterInit()'.
     */
    public function registerViisonCommonNamespaceWithCallback($callback = null)
    {
        // Save the passed callback for this plugin
        if (is_callable($callback)) {
            static::$namespaceLoadingCallbacks = (static::$namespaceLoadingCallbacks) ?: [];
            static::$namespaceLoadingCallbacks[get_class($this)] = $callback;
        }

        // Perform the main namespace registration
        $this->registerViisonCommonNamespace();
        if (!$this->isViisonCommonLoaded()) {
            return;
        }

        // Namespace is registered, hence call the callbacks exactly once
        if (!static::$namespaceLoadingCallbacksCalled) {
            static::$namespaceLoadingCallbacksCalled = true;
            foreach (static::$namespaceLoadingCallbacks as $loadingCallback) {
                $loadingCallback();
            }
        }
    }

    /**
     * This method is duplicated from Shopware\Plugins\ViisonCommon\Classes\AbstractSubscriberRegistrator, which is
     * necessary because the namespace might not be available when this method is called.
     *
     * @param SubscriberInterface $subscriber
     * @return boolean
     */
    protected function isSubscriberRegistered(SubscriberInterface $subscriber)
    {
        // Check the passed subscriber for any subscribed events
        $subscribedEvents = $subscriber::getSubscribedEvents();
        if (empty($subscribedEvents)) {
            return true;
        }

        // Get all currently registered event listeners using reflection. This is necessary, because calling
        // 'getListener()' or 'getAllListeners()' both trigger the *lazy loading* of the ContainerAwareEventManager
        // (which is not lazy at all) and causes all suscribers on services to be initialized. However, since this
        // method is executed really 'early' (before the main dispatch loop starts), some DI resource like 'shop' are
        // not yet available. If now a subscriber that is loaded *lazily* depends on one of these resources, the DI
        // container throws an exception (see also https://github.com/VIISON/ShopwarePickwareERP/issues/680).
        /** @var \Enlight_Event_EventManager $eventManager */
        $eventManager = $this->get('events');
        $reflection = new \ReflectionClass($eventManager);
        $property = $reflection->getProperty('listeners');
        $property->setAccessible(true);
        $listeners = $property->getValue($eventManager);

        // Use the first subscribed event to determine whether the passed subscriber is already registered
        $eventNames = array_keys($subscribedEvents);
        $eventName = mb_strtolower($eventNames[0]);
        if (!isset($listeners[$eventName])) {
            return false;
        }
        foreach ($listeners[$eventName] as $listener) {
            if ($listener instanceof \Enlight_Event_Handler_Default) {
                $listenerInstance = $listener->getListener();
                if (is_array($listenerInstance) && count($listenerInstance) > 0 && is_a($listenerInstance[0], get_class($subscriber))) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @return boolean True, if the ViisonCommon namespace is loaded and this plugin is installed and active. Otherwise false.
     */
    protected function isInstalledAndActive()
    {
        return $this->isViisonCommonLoaded() && Util::isPluginInstalledAndActive(null, $this->getName());
    }

    /**
     * @return boolean True, if and only if the ViisonCommon namespace is loaded. Otherwise false.
     */
    protected function isViisonCommonLoaded()
    {
        return $this->get('loader')->getClassPath('Shopware\\Plugins\\ViisonCommon\\Classes\\Util\\Util') !== null;
    }
}
