<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2011 - 2017 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flApp Class
 *
 * Application class.
 *
 * @package fusionLib
 */
class flApp {
	/**
	 * Instance of the application object.
	 *
	 * @var flApp
	 */
	static protected $instance = null;

	/**
	 * The configuration object.
	 *
	 * @var flConfig
	 */
	protected $cfg = null;

	/**
	 * The request object.
	 *
	 * @var flRequest
	 */
	protected $request = null;

	/**
	 * The router object.
	 *
	 * @var flRouter
	 */
	protected $router = null;

	/**
	 * The controller object.
	 *
	 * @var flController
	 */
	protected $controller = null;

	/**
	 * The response object.
	 *
	 * @var flResponse
	 */
	protected $response = null;

	/**
	 * The database object.
	 *
	 * @var flDb
	 */
	protected $db = null;

	/**
	 * Code buffer object.
	 *
	 * @var flBufferCode
	 */
	protected $codeBuffer = null;

	/**
	 * The cache object.
	 *
	 * @var flCache
	 */
	protected $cache = null;

	/**
	 * The session object.
	 *
	 * @var flSession
	 */
	protected $session = null;

	/**
	 * The event manager.
	 *
	 * @var flEventManager
	 */
	protected $eventManager = null;

	/**
	 * Inner driver objects e.g. Memcache
	 *
	 * @var array
	 */
	protected $innerObjects = array();

	/**
	 * Flags if secure cookies should be used.
	 *
	 * @var bool
	 */
	protected $secureCookies = false;

	/**
	 * Get the instance of the application object.
	 *
	 * @return flApp The app instance.
	 */
	static function getInstance() {
		return self::$instance !== null ? self::$instance : self::$instance = new flApp();
	}

	/**
	 * Set the instance of the application object to use.
	 *
	 * @param flApp $obj The application object.
	 * @return flApp
	 * @throws flAppException
	 */
	static function setInstance($obj) {
		if(self::$instance === null)
			self::$instance = $obj;
		else
			throw new flAppException('Application already initialized in setInstance.');
		return self::$instance;
	}

	/**
	 * Use secure cookies.
	 *
	 * @param bool $useSecure true if using secure cookies.
	 */
	static function useSecureCookies($useSecure = true) {
		self::getInstance()->secureCookies = $useSecure;
	}

	/**
	 * Test if using secure cookies.
	 *
	 * @return bool true if using secure cookies.
	 */
	static function usingSecureCookies() {
		return self::getInstance()->secureCookies;
	}

	/**
	 * Get the configuration object.
	 *
	 * @return flConfig
	 */
	static function getConfig() {
		return self::getInstance()->cfg;
	}

	/**
	 * Get the request object.
	 *
	 * @return flRequest
	 */
	static function getRequest() {
		return self::getInstance()->request;
	}

	/**
	 * Get the router object.
	 *
	 * @return flRouter
	 */
	static function getRouter() {
		return self::getInstance()->router;
	}

	/**
	 * Get the controller object.
	 *
	 * @return flController
	 */
	static function getController() {
		return self::getInstance()->controller;
	}

	/**
	 * Set the controller object.
	 *
	 * @param flController $controller The controller object.
	 * @return flApp This object.
	 */
	static function setController($controller) {
		$instance = self::getInstance();
		$instance->controller = $controller;
		return $instance;
	}

	/**
	 * Get the response object.
	 *
	 * @return flResponse
	 */
	static function getResponse() {
		return self::getInstance()->response;
	}

	/**
	 * Get the database object.
	 *
	 * @return flDb
	 */
	static function getDb() {
		return self::getInstance()->db;
	}

	/**
	 * Get the code buffer object.
	 *
	 * @return flBufferCode
	 */
	static function getCodeBuffer() {
		$instance = self::getInstance();
		if($instance->codeBuffer === null) {
			$instance->codeBuffer = new flBufferCode();
			$instance->codeBuffer->useJQuery($instance->cfg->get('usejQuery', false));
		}
		return $instance->codeBuffer;
	}

	/**
	 * Get the application cache object.
	 *
	 * @return flCache
	 */
	static function getCache() {
		return self::getInstance()->cache;
	}

	/**
	 * Get the session object.
	 *
	 * @return flSession
	 */
	static function getSession() {
		$instance = self::getInstance();
		if($instance->session === null) {
			// Setup sessions
			$life = $instance->cfg->get('sessionLife', null);
			$removeExit = $instance->cfg->get('sessionCookieSession', true);
			$name = $instance->cfg->get('sessionCookieName', '__sessionid');
			$domain = $instance->cfg->get('sessionCookieDomain', '');
			$path = $instance->cfg->get('sessionCookiePath', '/');

			switch($driver = 'flSession' . ($drv = $instance->cfg->get('sessionDriver', 'Db'))) {
				case 'flSessionMemcached':
				case 'flSessionMemcache':
				case 'flSessionRedis':
					$instance->session = new $driver($instance->response, isset($instance->innerObjects[$drv]) ?  $instance->innerObjects[$drv] : $instance->cfg->keyValueStore, $instance->db, $life, $removeExit, $name, $domain, $path, $instance->secureCookies);
					$instance->innerObjects[$drv] = $instance->session->_getInnerObject();
					break;
				case 'flSessionDb':
					$instance->session = new flSessionDb($instance->response, $instance->db, $life, $removeExit, $name, $domain, $path, $instance->secureCookies);
					break;
				default:
					$instance->session = new flSessionFile($instance->response, $life, $removeExit, $name, $domain, $path, $instance->secureCookies);
			}

			// If AJAX call then keep all flash data
			if($instance->request->isXmlHttpRequest())
				$instance->session->flashKeepAll();
		}
		return $instance->session;
	}

	/**
	 * Get the event manager.
	 *
	 * @return flEventManager The event manager.
	 */
	static function getEventManager() {
		$instance = self::getInstance();
		if($instance->eventManager === null)
			$instance->eventManager = new flEventManager($instance->db);
		return $instance->eventManager;
	}

	/**
	 * Initialise the application.
	 *
	 * @param flConfig $cfg The configuration object to use.
	 * @return flApp The app object
	 */
	static function init($cfg) {
		$instance = self::getInstance();

		$instance->cfg = $cfg;

		if(isset($instance->cfg->umask))
			umask($instance->cfg->umask);

		// If database info given create it
		if(isset($instance->cfg->db))
			$instance->db = flDb::connect($instance->cfg->db, isset($instance->cfg->dbslave) ? $instance->cfg->dbslave : null);

		// Setup template engine
		if($instance->cfg->get('templateEngineUseDb', $instance->db !== null))
			flTemplateEngine::setDB($instance->db);

		// Setup cache
		switch($driver = 'flCache' . ($drv = $instance->cfg->get('cacheDriver', 'File'))) {
			case 'flCacheMemcached':
			case 'flCacheMemcache':
			case 'flCacheRedis':
				$instance->cache = new $driver(isset($instance->innerObjects[$drv]) ?  $instance->innerObjects[$drv] : $instance->cfg->keyValueStore, $instance->cfg->get('cacheLifetime', 3600), $instance->cfg->get('cachePrefix', ''));
				$instance->innerObjects[$drv] = $instance->cache->_getInnerObject();
				break;
			case 'flCacheDb':
				$instance->cache = new flCacheDb($instance->db, $instance->cfg->get('cacheLifetime', 3600), $instance->cfg->get('cachePrefix', ''));
				break;
			default:
				$instance->cache = new $driver($instance->cfg->get('cacheLifetime', 3600), $instance->cfg->get('cachePrefix', ''));
				break;
		}

		if(isset($_SERVER['SERVER_NAME'])) {
			$instance->request = new flRequest($instance->cfg->basepath);

			// Request throttle
			if(isset($instance->cfg->throttleTime) && isset($instance->cfg->throttleLimit)) {
				$throttle = new flRequestThrottle($instance->db, $instance->cfg->throttleTime, $instance->cfg->throttleLimit);
				$throttle->requestAllowed($instance->request->getClientIp());
			}

			$instance->router = new flRouter($instance->request);
			$instance->response = new flResponse();
		}

		return $instance;
	}

	/**
	 * Run the application.
	 */
	static function run() {
		$instance = self::getInstance();
		$instance->router->route();
		$instance->controller = $instance->router->getController();
		$instance->controller->setResponse($instance->response)
			->dispatch($instance->router->getAction());
	}

	/**
	 * Test if the app is running form the console.
	 *
	 * @return bool true if console; else false for web.
	 */
	static function isConsole() {
		return !isset($_SERVER['SERVER_NAME']);
	}
}
