<?php
/**
 * UserFrosting (http://www.userfrosting.com)
 *
 * @link      https://github.com/userfrosting/UserFrosting
 * @license   https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
 */

namespace UserFrosting\System;

use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\Event;
use Slim\App;
use Slim\Container;
use UserFrosting\Support\Exception\FileNotFoundException;
use UserFrosting\System\Facade;

class UserFrosting
{
    /**
     * @var Container The global container object, which holds all your services.
     */
    protected $ci;

    /**
     * @var App The Slim application instance.
     */
    protected $app;

    /**
     * Create the UserFrosting application instance.
     */
    public function __construct() {
        // First, we create our DI container
        $this->ci = new Container;

        // Set up facade reference to container.
        Facade::setFacadeContainer($this->ci);
    }

    /**
     * Fires an event with optional parameters.
     *
     * @param  string $eventName
     * @param  Event $event
     *
     * @return Event
     */
    public function fireEvent($eventName, Event $event = NULL) {
        /** @var EventDispatcher $events */
        $eventDispatcher = $this->ci->eventDispatcher;

        return $eventDispatcher->dispatch($eventName, $event);
    }

    /**
     * Return the underlying Slim App instance, if available.
     *
     * @return Slim\App
     */
    public function getApp() {
        return $this->app;
    }

    /**
     * Return the DI container.
     *
     * @return Slim\Container
     */
    public function getContainer() {
        return $this->ci;
    }

    /**
     * Include all defined routes in route stream.
     *
     * Include them in reverse order to allow higher priority routes to override lower priority.
     */
    public function loadRoutes() {
        // Since routes aren't encapsulated in a class yet, we need this workaround :(
        global $app;
        $app = $this->app;

        $routePaths = array_reverse($this->ci->locator->findResources('routes://', TRUE, TRUE));
        foreach ($routePaths as $path) {
            $routeFiles = glob($path . '/*.php');
            foreach ($routeFiles as $routeFile) {
                require_once $routeFile;
            }
        }
    }

    /**
     * Initialize the application.  Set up Sprinkles and the Slim app, define routes, register global middleware, and run Slim.
     */
    public function run() {
        $this->setupSprinkles();

        // Set the configuration settings for Slim in the 'settings' service
        $this->ci->settings = $this->ci->config['settings'];

        // Next, we'll instantiate the Slim application.  Note that the application is required for the SprinkleManager to set up routes.
        $this->app = new App($this->ci);

        $slimAppEvent = new SlimAppEvent($this->app);

        $this->fireEvent('onAppInitialize', $slimAppEvent);

        // Set up all routes
        $this->loadRoutes();

        // Add global middleware
        $this->fireEvent('onAddGlobalMiddleware', $slimAppEvent);

        $this->app->run();
    }

    /**
     * Register system services, load all sprinkles, and add their resources and services.
     *
     * @param bool $isWeb Set to true if setting up in an HTTP/web environment, false if setting up for CLI scripts.
     */
    public function setupSprinkles($isWeb = TRUE) {
        // Register system services
        $serviceProvider = new ServicesProvider();
        $serviceProvider->register($this->ci);

        // Boot the Sprinkle manager, which creates Sprinkle classes and subscribes them to the event dispatcher
        $sprinkleManager = $this->ci->sprinkleManager;

        try {
            $sprinkleManager->initFromSchema(\UserFrosting\SPRINKLES_SCHEMA_FILE);
        } catch (FileNotFoundException $e) {
            if ($isWeb) {
                $this->renderSprinkleErrorPage($e->getMessage());
            } else {
                $this->renderSprinkleErrorCli($e->getMessage());
            }
        }

        $this->fireEvent('onSprinklesInitialized');

        // Add Sprinkle resources (assets, templates, etc) to locator
        $sprinkleManager->addResources();
        $this->fireEvent('onSprinklesAddResources');

        // Register Sprinkle services
        $sprinkleManager->registerAllServices();
        $this->fireEvent('onSprinklesRegisterServices');
    }

    /**
     * Render a basic error page for problems with loading Sprinkles.
     */
    protected function renderSprinkleErrorPage($errorMessage = "") {
        ob_clean();
        $title = "UserFrosting Application Error";
        $errorMessage = "Unable to start site. Contact owner.<br/><br/>" .
            "Version: UserFrosting " . \UserFrosting\VERSION . "<br/>" .
            $errorMessage;
        $output = sprintf(
            "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" .
            "<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," .
            "sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" .
            "display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>",
            $title,
            $title,
            $errorMessage
        );
        exit($output);
    }

    /**
     * Render a CLI error message for problems with loading Sprinkles.
     */
    protected function renderSprinkleErrorCli($errorMessage = "") {
        exit($errorMessage . PHP_EOL);
    }
}