<?php
namespace IntellexApps\PHP\UploadFile\Routing;

use IntellexApps\PHP\UploadFile\Domain\Service\ServiceContainer;
use IntellexApps\PHP\UploadFile\Domain\Service\UploadFileService;
use IntellexApps\PHP\UploadFile\Network\JsonResponse;
use IntellexApps\PHP\UploadFile\Network\Request;
use IntellexApps\PHP\UploadFile\Network\Response;
use IntellexApps\PHP\UploadFile\Util\Inflector;

require_once __DIR__ . '/../Config/bootstrap.php';

class Dispatcher {

    /** @var null Request */
    private $request = null;

    /** @var null Response */
    private $response = null;

    /**
     * Dispatch new request
     */
    public function dispatch()
    {
        // Initialize request
        $this->request = new Request();

        // Initialize response
        $this->response = new Response();

        // Set request in router
        Router::setRequest($this->request);

        // Set response in router
        Router::setResponse($this->response);

        // TODO: Move this to client code!!!!
        ServiceContainer::instance()->get('upload')->configure();

        // Handle request
        $this->handleRequest();
    }

    /**
     * Handle request
     */
    private function handleRequest()
    {

        // Get route parts
        $chunks = Router::getRouteParts();

        // Define default controller and action names
        $controllerName = 'Index';
        $actionName = 'index';

        // Check if controller set
        if(isset($chunks[0])) {
            $controllerName = Inflector::classify($chunks[0]);
        }

        // Check if action is set
        if(isset($chunks[1])) {
            $actionName = Inflector::camel($chunks[1]);
        }

        $controller = $this->getControllerInstance($controllerName);

        if(!$controller) {
            throw new ControllerNotFoundException($controllerName);
        }

        $this->executeAction($controller, $actionName);

    }

    /**
     * Get controller instance for controller name
     *
     * @param $controllerName
     * @return null
     */
    private function getControllerInstance($controllerName)
    {
        $className = GLOBAL_NAMESPACE . "\\Controller\\" . $controllerName . 'Controller';
        if(class_exists($className)) {
            return new $className(
                $controllerName,
                $this->request,
                $this->response
            );
        }
        return null;
    }

    /**
     * Execute controller action
     *
     * @param $controller
     * @param $actionName
     * @throws ControllerActionNotFound
     */
    private function executeAction($controller, $actionName)
    {
        // Build method name
        $methodName = $actionName . 'Action';

        // Check if method exists
        if(!method_exists($controller, $methodName)) {
            throw new ControllerActionNotFound($controller, $methodName);
        }

        // Get response
        $result = $controller->$methodName();

        // If type of response is Response, call output
        if($result instanceof Response) {
            $result->output();
        } else {
            $response = $this->getDefaultResponse();
            $response->setContent($result);
            $response->output();
        }
    }


    /**
     * Get default response
     *
     * @return Response
     */
    private function getDefaultResponse()
    {
        return new JsonResponse();
    }
}


class ControllerNotFoundException extends \Exception {

    public function __construct($controllerName)
    {
        parent::__construct(__("Controller %s not found", $controllerName));
    }
}
class ControllerActionNotFound extends \Exception {


    public function __construct($controller, $actionName)
    {
        parent::__construct(__("Method %s not found in controller %s", $actionName, $controller->getName()));
    }
}
