<?php
/**
 * Enter description here...
 *
 * @package aowp.aspect
 */

/**
 * Enter description here...
 *
 * @package aowp.aspect
 * @uses AOWP_AspectManager
 */
class AOWP_AspectInstanceManager {
	
	const DATA_FOLDER_NAME = 'AOWP_AspectInstanceManager';
	const APPLICATION_SCOPE_INSTANCE_SERIALIZATION_FOLDER = 'application';
	const LOCK_FILE_CHECK_INTERVAL = 100000;
	const SESSION_SCOPE_INSTANCE_SAVE_FOLDER = 'session';
	const AOWP_SESSION_ID = 'AOWP_SESSION';
	const AOWP_SESSION_ID_EXPIRE = 2592000;
	const AOWP_SESSION_INSTANCE_LIST_NAME = 'instance_list.txt';
	
	private static $_REQUEST_SCOPE_INSTANCE_ARRAY = array();
	private static $_SESSION_INSTANCE_CACHE_ARRAY = array();
	
	private static function getDataFolderPath() {
		return AOWP_ConfigurationManager::getRuntimeDataFolder() . DIRECTORY_SEPARATOR . AOWP_AspectInstanceManager::DATA_FOLDER_NAME . DIRECTORY_SEPARATOR;
	}
	
	public static function getInstance($aspectClassName) {
		if (class_exists($aspectClassName) || AOWP_AspectManager::includeOnceAspect($aspectClassName)) {
			if (new $aspectClassName instanceof AOWP_IPerApplicationAspect) {
				return AOWP_AspectInstanceManager::_getApplicationScopeInstance($aspectClassName);
			}
			else if (new $aspectClassName instanceof AOWP_IPerSessionAspect) {
				return AOWP_AspectInstanceManager::_getSessionScopeInstance($aspectClassName);
			}
			else if (new $aspectClassName instanceof AOWP_IPerRequestAspect) {
				return AOWP_AspectInstanceManager::_getRequestScopeInstance($aspectClassName);
			}
			else {
				return new $aspectClassName;
			}
		}
		else {
			return null;
		}
	}
	public static function releaseInstance(AOWP_Aspect $aspectInstance) {
		if ($aspectInstance instanceof AOWP_IPerApplicationAspect) {
			AOWP_AspectInstanceManager::_releaseApplicationScopeInstance($aspectInstance);
		}
		else if ($aspectInstance instanceof AOWP_IPerSessionAspect) {
			AOWP_AspectInstanceManager::_releaseSessionScopeInstance($aspectInstance);
		}
		else if ($aspectInstance instanceof AOWP_IPerRequestAspect) {
			
		}
		else {
			
		}
	}
	
	private static function _getSerializedApplicationScopeInstancePath($aspectClassName) {
		$applicationFolder = AOWP_AspectInstanceManager::getDataFolderPath() . AOWP_AspectInstanceManager::APPLICATION_SCOPE_INSTANCE_SERIALIZATION_FOLDER;
		if (!file_exists($applicationFolder)) {
			AOWP_PHPFileManager::mkdir($applicationFolder);
		}
		return $applicationFolder . DIRECTORY_SEPARATOR . $aspectClassName;
	}
	private static function _getApplicationScopeInstance($aspectClassName) {
//		AOWP_Logger::logToFile('Get a per-application instance [' . $aspectClassName . ': ' . AOWP_AspectInstanceManager::getSessionID() . '].');
		$returnedInstance = null;
		while ($returnedInstance == null) {
			$serializedInstancePath = AOWP_AspectInstanceManager::_getSerializedApplicationScopeInstancePath($aspectClassName);
			$serializedInstancePointer = fopen($serializedInstancePath, 'r+');
			flock($serializedInstancePointer, LOCK_EX);
			$serializedInstanceData = file_get_contents($serializedInstancePath);
			$returnedInstance = unserialize($serializedInstanceData);
		}
//		AOWP_Logger::logToFile('RETURNED APPLICATION SCOPE INSTANCE [' . (is_object($returnedInstance) ? get_class($returnedInstance) : 'NULL') . '].');
		return $returnedInstance;
	}
	private static function _releaseApplicationScopeInstance(AOWP_IPerApplicationAspect $aspectInstance) {
		$aspectClassName = get_class($aspectInstance);
		$serializedInstancePath = AOWP_AspectInstanceManager::_getSerializedApplicationScopeInstancePath($aspectClassName);
//		AOWP_Logger::logToFile('Release a per-application instance [' . $aspectClassName . ': ' . AOWP_AspectInstanceManager::getSessionID() . '].');
		// Per-application aspect instance is created first at weaving, and the time of creation does not require file lock.
		$serializedInstancePointer = null;
		if (file_exists($serializedInstancePath)) {
			$serializedInstancePointer = fopen($serializedInstancePath, 'r+');
		}
		$serializedInstanceData = serialize($aspectInstance);
		AOWP_PHPFileManager::saveFile($serializedInstancePath, $serializedInstanceData);
		if ($serializedInstancePointer != null) {
			flock($serializedInstancePointer, LOCK_UN);
		}
	}
	
	public static function getSessionID() {
		if (isset($_COOKIE[AOWP_AspectInstanceManager::AOWP_SESSION_ID])) {
			return $_COOKIE[AOWP_AspectInstanceManager::AOWP_SESSION_ID];
		}
		else {
			return AOWP_AspectInstanceManager::_setSessionID();
		}
		
	}
	private static function _setSessionID($sessionID = null) {
		$sessionID = $sessionID === null ? microtime(true) : $sessionID;
		setcookie(AOWP_AspectInstanceManager::AOWP_SESSION_ID, microtime(true), time() + AOWP_AspectInstanceManager::AOWP_SESSION_ID_EXPIRE);
		$_COOKIE[AOWP_AspectInstanceManager::AOWP_SESSION_ID] = $sessionID;
		return $sessionID;
	}
	
	private static function _checkSessionScopeInstanceAvailable($aspectClassName) {
		return isset($_COOKIE[strtolower($aspectClassName)]);
	}
	public static function setSessionScopeInstanceSessionTimeOut(AOWP_PerSessionAspect $aspect) {
		$timeOutDuration = $aspect->getSessionTimeOut() === 0 ? 0 : time() + $aspect->getSessionTimeOut();
		setcookie(strtolower(get_class($aspect)), '1', $timeOutDuration);
	}
	private static function _getSessionScopeInstanceSaveFolder() {
		$sessionID = AOWP_AspectInstanceManager::getSessionID();
		$sessionScopeInstanceFolder = AOWP_AspectInstanceManager::getDataFolderPath() .
			AOWP_AspectInstanceManager::SESSION_SCOPE_INSTANCE_SAVE_FOLDER . DIRECTORY_SEPARATOR .
			$sessionID . DIRECTORY_SEPARATOR;
		if (!file_exists($sessionScopeInstanceFolder)) {
			AOWP_PHPFileManager::mkdir($sessionScopeInstanceFolder);
		}
		return $sessionScopeInstanceFolder;
	}
	private static function _releaseSessionScopeInstance(AOWP_PerSessionAspect $aspectInstance) {
		$serializationInstancePath = AOWP_AspectInstanceManager::_getSessionScopeInstanceSaveFolder() . get_class($aspectInstance);
		AOWP_PHPFileManager::saveFile($serializationInstancePath, serialize($aspectInstance));
		AOWP_AspectInstanceManager::$_SESSION_INSTANCE_CACHE_ARRAY[get_class($aspectInstance)] = $aspectInstance;
	}
	private static function _getSessionScopeInstance($aspectClassName) {
		$sessionScopeInstanceSaveFolder = AOWP_AspectInstanceManager::_getSessionScopeInstanceSaveFolder();
		$instanceSerializationFile = $sessionScopeInstanceSaveFolder . $aspectClassName;
		if (($cachedInstance = AOWP_AspectInstanceManager::_getSessionScopeInstanceCache($aspectClassName)) !== null) {
			return $cachedInstance;
		}
		else if (!AOWP_AspectInstanceManager::_checkSessionScopeInstanceAvailable($aspectClassName) || !file_exists($instanceSerializationFile)) {
			$aspectInstance = new $aspectClassName;
			AOWP_AspectInstanceManager::setSessionScopeInstanceSessionTimeOut($aspectInstance);
			return $aspectInstance;
		}
		else {
			return unserialize(file_get_contents($instanceSerializationFile));
		}
	}
	private static function _getSessionScopeInstanceCache($aspectName) {
		if (isset(AOWP_AspectInstanceManager::$_SESSION_INSTANCE_CACHE_ARRAY[$aspectName])) {
			return AOWP_AspectInstanceManager::$_SESSION_INSTANCE_CACHE_ARRAY[$aspectName];
		}
		else {
			return null;
		}
	}
	private static function _getAllSessionScopeInstanceArray() {
		$sessionScopeInstanceArray = array();
		$allAspectFileArray = AOWP_PHPFileManager::getAllPHPFile(AOWP_AspectInstanceManager::_getSessionScopeInstanceSaveFolder(), AOWP_PHPFileManager::ASPECT_FILE_PATTERN);
		foreach ($allAspectFileArray as $aspectFile) {
			$sessionScopeInstanceArray[] = unserialize($aspectFile);
		}
		return $sessionScopeInstanceArray;
	}
	
	public static function updateSessionScopeTimeOut() {
		$sessionScopeInstanceArray = AOWP_AspectInstanceManager::_getAllSessionScopeInstanceArray();
		foreach ($sessionScopeInstanceArray as $sessionScopeInstance) {
			AOWP_AspectInstanceManager::setSessionScopeInstanceSessionTimeOut($sessionScopeInstance);
		}
	}
	
	private static function _getRequestScopeInstance($aspectClassName) {
		if (array_key_exists($aspectClassName, AOWP_AspectInstanceManager::$_REQUEST_SCOPE_INSTANCE_ARRAY)) {
			return AOWP_AspectInstanceManager::$_REQUEST_SCOPE_INSTANCE_ARRAY[$aspectClassName];
		}
		else {
			$instance = new $aspectClassName;
			AOWP_AspectInstanceManager::$_REQUEST_SCOPE_INSTANCE_ARRAY[$aspectClassName] = $instance;
			return $instance;
		}
	}
	
}
?>