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

namespace Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility;

use Shopware\Components\DependencyInjection\Container;
use Shopware\Components\DependencyInjection\ContainerAwareInterface;
use \Enlight_Controller_Front as Front;
use \Enlight_Controller_Request_Request as Request;
use \Enlight_Controller_Response_Response as Response;
use \Enlight_View_Default as View;

// phpcs:ignore VIISON.Classes.AbstractClassName -- cannot change name to keep backwards compatiblity
abstract class Layer implements ContainerAwareInterface
{
    /**
     * @var \Enlight_Controller_Request_Request $request
     */
    protected $request;

    /**
     * @var \Enlight_Controller_Response_Response $response
     */
    protected $response;

    /**
     * @var \Enlight_View_Default $view
     */
    protected $view;

    /**
     * @var \Enlight_Controller_Front $front
     */
    protected $front;

    /**
     * @var boolean $blockPostDispatch
     */
    protected $blockPostDispatch = false;

    /**
     * @var \Shopware\Components\DependencyInjection\Container $container
     */
    protected $container;

    /**
     * @var \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer $predecessor
     */
    private $predecessor;

    /**
     * @var \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer $successor
     */
    private $successor;

    /**
     * Returns the version of the layer as a date string having the format 'YYYY-MM-DD' ('Y-m-d').
     * The version determines that all requests, which require a version smaller or equal the version
     * returned by this layer, must be routed through this layer to reach compatibility.
     *
     * @return string
     */
    abstract public function getVersion();

    /**
     * Overwrite this method to control the execution order of a layer, if several layers
     * exist for the same version.
     *
     * @return int
     */
    public function getPosition()
    {
        return 0;
    }

    /**
     * Configures this layer using the given request, response, view and front instances.
     * If this layer has a successor, the configuration is passed on to it.
     *
     * @param \Enlight_Controller_Request_Request $request
     * @param \Enlight_Controller_Response_Response $response
     * @param \Enlight_View_Default $view
     * @param \Enlight_Controller_Front $front
     */
    public function configure(Request $request, Response $response, View $view, Front $front)
    {
        $this->request = $request;
        $this->response = $response;
        $this->view = $view;
        $this->front = $front;
        if ($this->successor) {
            $this->successor->configure($this->request, $this->response, $this->view, $this->front);
        }
    }

    /**
     * @return \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer
     */
    public function getPredecessor()
    {
        return $this->predecessor;
    }

    /**
     * @param \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer $predecessor
     */
    public function setPredecessor($predecessor)
    {
        $this->predecessor = $predecessor;
    }

    /**
     * @return \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer
     */
    public function getSuccessor()
    {
        return $this->successor;
    }

    /**
     * Saves the given successive layer and in return sets this instance as that
     * successor's predecessor.
     *
     * @param \Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer $successor
     */
    public function setSuccessor(Layer $successor)
    {
        $this->successor = $successor;
        if ($this->successor) {
            $this->successor->setPredecessor($this);
        }
    }

    /**
     * @param \Shopware\Components\DependencyInjection\Container $container
     */
    public function setContainer(Container $container = null)
    {
        $this->container = $container;
        if ($this->successor) {
            $this->successor->setContainer($container);
        }
    }

    /**
     * Use this method to get a service from the DI container.
     *
     * @param string $name
     * @return mixed
     */
    public function get($name)
    {
        return $this->container->get($name);
    }

    /**
     * Note: You can override this method in your implemented Layer to disable the output buffering for certain requests.
     *       Make sure to call the parent method and add its result to the return value using a logical OR.
     *
     * @return boolean True, if this layer has a successor that requires a the output buffering to be disabled. Otherwise files.
     */
    public function requiresDisabledOutputBuffering()
    {
        return $this->successor && $this->successor->requiresDisabledOutputBuffering();
    }

    /**
     * @return boolean True, if this layer's 'blockPostDispatch' flag is set of it has a successor that blocks the post dispatch. Otherwise files.
     */
    public function blocksPostDispatch()
    {
        return $this->blockPostDispatch || ($this->successor && $this->successor->blocksPostDispatch());
    }

    /**
     * Tries to perform the 'pre' method of the action with the given name and, if
     * this layer has a successor, lets that successor perform the 'pre' action too.
     *
     * @param string $actionName
     */
    public function performPreAction($actionName)
    {
        // Let this layer perform the pre action
        $this->performMethodIfExists('pre' . ucfirst($actionName));

        // Let the successor perform the pre action
        if ($this->successor) {
            $this->successor->performPreAction($actionName);
        }
    }

    /**
     * Tries to perform the 'post' method of the action with the given name and, if
     * this layer has a predecessor, lets that predecessor perform the 'post' action too.
     *
     * @param string $actionName
     */
    public function performPostAction($actionName)
    {
        // Let this layer perform the post action
        $this->performMethodIfExists('post' . ucfirst($actionName));

        // Let the predecessor perform the post action
        if ($this->predecessor) {
            $this->predecessor->performPostAction($actionName);
        }
    }

    /**
     * Checks whether this instance has method with the given name and, if it does,
     * calls it without arguments.
     *
     * @param string $methodName
     */
    private function performMethodIfExists($methodName)
    {
        if (method_exists($this, $methodName)) {
            $this->$methodName();
        }
    }
}
