<?php
/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\Mvc\Controller;

use 
Zend\EventManager\EventInterface as Event;
use 
Zend\EventManager\EventManager;
use 
Zend\EventManager\EventManagerAwareInterface;
use 
Zend\EventManager\EventManagerInterface;
use 
Zend\Http\PhpEnvironment\Response as HttpResponse;
use 
Zend\Http\Request as HttpRequest;
use 
Zend\Mvc\InjectApplicationEventInterface;
use 
Zend\Mvc\MvcEvent;
use 
Zend\ServiceManager\ServiceLocatorAwareInterface;
use 
Zend\ServiceManager\ServiceLocatorInterface;
use 
Zend\Stdlib\DispatchableInterface as Dispatchable;
use 
Zend\Stdlib\RequestInterface as Request;
use 
Zend\Stdlib\ResponseInterface as Response;

/**
 * Abstract controller
 *
 * Convenience methods for pre-built plugins (@see __call):
 *
 * @method \Zend\View\Model\ModelInterface acceptableViewModelSelector(array $matchAgainst = null, bool $returnDefault = true, \Zend\Http\Header\Accept\FieldValuePart\AbstractFieldValuePart $resultReference = null)
 * @method bool|array|\Zend\Http\Response fileprg(\Zend\Form\Form $form, $redirect = null, $redirectToUrl = false)
 * @method bool|array|\Zend\Http\Response filePostRedirectGet(\Zend\Form\Form $form, $redirect = null, $redirectToUrl = false)
 * @method \Zend\Mvc\Controller\Plugin\FlashMessenger flashMessenger()
 * @method \Zend\Mvc\Controller\Plugin\Forward forward()
 * @method mixed|null identity()
 * @method \Zend\Mvc\Controller\Plugin\Layout|\Zend\View\Model\ModelInterface layout(string $template = null)
 * @method \Zend\Mvc\Controller\Plugin\Params|mixed params(string $param = null, mixed $default = null)
 * @method \Zend\Http\Response|array prg(string $redirect = null, bool $redirectToUrl = false)
 * @method \Zend\Http\Response|array postRedirectGet(string $redirect = null, bool $redirectToUrl = false)
 * @method \Zend\Mvc\Controller\Plugin\Redirect redirect()
 * @method \Zend\Mvc\Controller\Plugin\Url url()
 */
abstract class AbstractController implements
    
Dispatchable,
    
EventManagerAwareInterface,
    
InjectApplicationEventInterface,
    
ServiceLocatorAwareInterface
{
    
/**
     * @var PluginManager
     */
    
protected $plugins;

    
/**
     * @var Request
     */
    
protected $request;

    
/**
     * @var Response
     */
    
protected $response;

    
/**
     * @var Event
     */
    
protected $event;

    
/**
     * @var EventManagerInterface
     */
    
protected $events;

    
/**
     * @var ServiceLocatorInterface
     */
    
protected $serviceLocator;

    
/**
     * @var string
     */
    
protected $eventIdentifier;

    
/**
     * Execute the request
     *
     * @param  MvcEvent $e
     * @return mixed
     */
    
abstract public function onDispatch(MvcEvent $e);


    
/**
     * Dispatch a request
     *
     * @events dispatch.pre, dispatch.post
     * @param  Request $request
     * @param  null|Response $response
     * @return Response|mixed
     */
    
public function dispatch(Request $requestResponse $response null)
    {
        
$this->request $request;
        if (!
$response) {
            
$response = new HttpResponse();
        }
        
$this->response $response;

        
$e $this->getEvent();
        
$e->setRequest($request)
          ->
setResponse($response)
          ->
setTarget($this);

        
$result $this->getEventManager()->trigger(MvcEvent::EVENT_DISPATCH$e, function ($test) {
            return (
$test instanceof Response);
        });

        if (
$result->stopped()) {
            return 
$result->last();
        }

        return 
$e->getResult();
    }

    
/**
     * Get request object
     *
     * @return Request
     */
    
public function getRequest()
    {
        if (!
$this->request) {
            
$this->request = new HttpRequest();
        }

        return 
$this->request;
    }

    
/**
     * Get response object
     *
     * @return Response
     */
    
public function getResponse()
    {
        if (!
$this->response) {
            
$this->response = new HttpResponse();
        }

        return 
$this->response;
    }

    
/**
     * Set the event manager instance used by this context
     *
     * @param  EventManagerInterface $events
     * @return AbstractController
     */
    
public function setEventManager(EventManagerInterface $events)
    {
        
$events->setIdentifiers(array(
            
'Zend\Stdlib\DispatchableInterface',
            
__CLASS__,
            
get_called_class(),
            
$this->eventIdentifier,
            
substr(get_called_class(), 0strpos(get_called_class(), '\\'))
        ));
        
$this->events $events;
        
$this->attachDefaultListeners();

        return 
$this;
    }

    
/**
     * Retrieve the event manager
     *
     * Lazy-loads an EventManager instance if none registered.
     *
     * @return EventManagerInterface
     */
    
public function getEventManager()
    {
        if (!
$this->events) {
            
$this->setEventManager(new EventManager());
        }

        return 
$this->events;
    }

    
/**
     * Set an event to use during dispatch
     *
     * By default, will re-cast to MvcEvent if another event type is provided.
     *
     * @param  Event $e
     * @return void
     */
    
public function setEvent(Event $e)
    {
        if (
$e instanceof Event && !$e instanceof MvcEvent) {
            
$eventParams $e->getParams();
            
$e = new MvcEvent();
            
$e->setParams($eventParams);
            unset(
$eventParams);
        }
        
$this->event $e;
    }

    
/**
     * Get the attached event
     *
     * Will create a new MvcEvent if none provided.
     *
     * @return MvcEvent
     */
    
public function getEvent()
    {
        if (!
$this->event) {
            
$this->setEvent(new MvcEvent());
        }

        return 
$this->event;
    }

    
/**
     * Set serviceManager instance
     *
     * @param  ServiceLocatorInterface $serviceLocator
     * @return void
     */
    
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        
$this->serviceLocator $serviceLocator;
    }

    
/**
     * Retrieve serviceManager instance
     *
     * @return ServiceLocatorInterface
     */
    
public function getServiceLocator()
    {
        return 
$this->serviceLocator;
    }

    
/**
     * Get plugin manager
     *
     * @return PluginManager
     */
    
public function getPluginManager()
    {
        if (!
$this->plugins) {
            
$this->setPluginManager(new PluginManager());
        }

        
$this->plugins->setController($this);
        return 
$this->plugins;
    }

    
/**
     * Set plugin manager
     *
     * @param  PluginManager $plugins
     * @return AbstractController
     */
    
public function setPluginManager(PluginManager $plugins)
    {
        
$this->plugins $plugins;
        
$this->plugins->setController($this);

        return 
$this;
    }

    
/**
     * Get plugin instance
     *
     * @param  string     $name    Name of plugin to return
     * @param  null|array $options Options to pass to plugin constructor (if not already instantiated)
     * @return mixed
     */
    
public function plugin($name, array $options null)
    {
        return 
$this->getPluginManager()->get($name$options);
    }

    
/**
     * Method overloading: return/call plugins
     *
     * If the plugin is a functor, call it, passing the parameters provided.
     * Otherwise, return the plugin instance.
     *
     * @param  string $method
     * @param  array  $params
     * @return mixed
     */
    
public function __call($method$params)
    {
        
$plugin $this->plugin($method);
        if (
is_callable($plugin)) {
            return 
call_user_func_array($plugin$params);
        }

        return 
$plugin;
    }

    
/**
     * Register the default events for this controller
     *
     * @return void
     */
    
protected function attachDefaultListeners()
    {
        
$events $this->getEventManager();
        
$events->attach(MvcEvent::EVENT_DISPATCH, array($this'onDispatch'));
    }

    
/**
     * Transform an "action" token into a method name
     *
     * @param  string $action
     * @return string
     */
    
public static function getMethodFromAction($action)
    {
        
$method  str_replace(array('.''-''_'), ' '$action);
        
$method  ucwords($method);
        
$method  str_replace(' '''$method);
        
$method  lcfirst($method);
        
$method .= 'Action';

        return 
$method;
    }
}