<?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.

use Shopware\Components\DependencyInjection\Container;
use Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\ControllerWrapper;

class Shopware_Controllers_Api_ViisonPickwareCommonApiRequestCompatibilityContainer extends Shopware_Controllers_Api_Rest
{
    /**
     * @var \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\ControllerWrapper $controllerWrapper
     */
    private $controllerWrapper;

    /**
     * @var string $originalActionName
     */
    private $originalActionName;

    /**
     * Creates a new controller wrapper using the entry layer provided by the compatibility manager.
     * Furthermore saves the original action name and replaces it in the request with the name of
     * the dynamic dispatch action.
     *
     * @throws \Exception If the designated compatibility entry layer does not exist.
     */
    public function init()
    {
        // Try to find compatibility layer for the requested controller
        $apiCompatibilityManager = Shopware()->Container()->get('viison_pickware_common.api_request_compatibility_manager');
        $entryLayer = $apiCompatibilityManager->getEntryLayerForRequest($this->request);
        if (!$entryLayer) {
            throw new Exception('Required the API request compatibility container, but the designated entry layer does not exist.');
        }

        // Create a controller wrapper using the entry layer
        $this->controllerWrapper = new ControllerWrapper($entryLayer, $this->request, $this->response, $this->view, $this->front);

        // Starting with Shopware 5.7.6 the container is set on the controller before it is initialized, hence we must
        // pass the container on to the controller wrapper, if available
        // (Change in Shopware: https://github.com/shopware/shopware/commit/255659105545afbf2b5f892f36847a2c49ba43e9)
        if ($this->container) {
            $this->controllerWrapper->setContainer($this->container);
        }

        // Disable output buffering if required by the layer
        if ($entryLayer->requiresDisabledOutputBuffering()) {
            $this->front->setParam('disableOutputBuffering', true);
        }

        // Re-route to dynamic dispatch
        $this->originalActionName = $this->request->getActionName();
        $this->request->setActionName('dynamicDispatch');
    }

    /**
     * Passes the given DI container to the controller wrapper.
     *
     * @param \Shopware\Components\DependencyInjection\Container $container
     */
    public function setContainer(Container $container = null)
    {
        // Starting with Shopware 5.7.6 the container is set on the controller before it is initialized, hence we must
        // only pass the container on to the controller wrapper, if it is already available (which is only the case when
        // running Shopware <= 5.7.5)
        // (Change in Shopware: https://github.com/shopware/shopware/commit/255659105545afbf2b5f892f36847a2c49ba43e9)
        if ($this->controllerWrapper) {
            $this->controllerWrapper->setContainer($container);
        }

        parent::setContainer($container);
    }

    /**
     * Resets the request's controller and action names (Shopware's default controller and method selection
     * is complete when 'preDispatch()' is called). This hides the compatibility layer and this controller
     * from the original controller. Finally calls the 'preDispatch()' method on the target controller.
     */
    public function preDispatch()
    {
        // Reset controller and action names respecting custom API routing
        $this->request->setActionName($this->originalActionName);
        $this->request->setControllerName($this->request->getParam('controller'));

        // Re-route if necessary
        $apiRouter = $this->get('pickware.erp.rest_api_router_service');
        $controllerName = $apiRouter->getControllerNameForRequest($this->request);
        $this->request->setControllerName($controllerName);

        // Pass on 'preDispatch()'
        $this->controllerWrapper->getRequestedController()->preDispatch();
    }

    /**
     * Checks whether the entry layer (or any of its successors) blocks the post dispatch and, if it doesn't,
     * calls the the 'postDispatch()' method on the target controller.
     */
    public function postDispatch()
    {
        if (!$this->controllerWrapper->getEntryLayer()->blocksPostDispatch()) {
            $this->controllerWrapper->getRequestedController()->postDispatch();
        }
    }

    /**
     * Uses the compatibility controller wrapper to dispatch the original action.
     */
    public function dynamicDispatchAction()
    {
        $this->controllerWrapper->dispatch($this->request->getActionName());
    }
}
