<?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 function getDataFolderPath() {
		return AOWP_ConfigurationManager::getRuntimeDataFolder() . DIRECTORY_SEPARATOR . AOWP_AspectInstanceManager::DATA_FOLDER_NAME . DIRECTORY_SEPARATOR;
	}
	
	public static function getInstance($aspectClassName) {
		if (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 _getApplicationScopeInstanceLockPath($aspectClassName) {
		return AOWP_AspectInstanceManager::_getSerializedApplicationScopeInstancePath($aspectClassName) . '.lock';
	}
	private static function _lockApplicationScopeInstance($aspectClassName) {
		$lockFilePath = AOWP_AspectInstanceManager::_getApplicationScopeInstanceLockPath($aspectClassName);
		$i = 0;
		while (file_exists($lockFilePath)) {
			usleep(AOWP_AspectInstanceManager::LOCK_FILE_CHECK_INTERVAL);
			if ($i++ > 10) {
				echo 'Dead lock.';
				exit();
			}
		}
		AOWP_PHPFileManager::saveFile($lockFilePath, '');
	}
	private static function _unlockApplicationScopeInstance($aspectClassName) {
		unlink(AOWP_AspectInstanceManager::_getApplicationScopeInstanceLockPath($aspectClassName));
	}
	private static function _getApplicationScopeInstance($aspectClassName) {
		$serializedInstancePath = AOWP_AspectInstanceManager::_getSerializedApplicationScopeInstancePath($aspectClassName);
		AOWP_AspectInstanceManager::_lockApplicationScopeInstance($aspectClassName);
		if (file_exists($serializedInstancePath)) {
			$serializedInstanceData = file_get_contents($serializedInstancePath);
			return unserialize($serializedInstanceData);
		}
		else {
			return new $aspectClassName;
		}
	}
	private static function _releaseApplicationScopeInstance(AOWP_IPerApplicationAspect $aspectInstance) {
		$aspectClassName = get_class($aspectInstance);
		$serializedInstanceData = serialize($aspectInstance);
		AOWP_PHPFileManager::saveFile(AOWP_AspectInstanceManager::_getSerializedApplicationScopeInstancePath($aspectClassName), $serializedInstanceData);
		AOWP_AspectInstanceManager::_unlockApplicationScopeInstance($aspectClassName);
	}
	
	private 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);
		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));
	}
	private static function _getSessionScopeInstance($aspectClassName) {
		$sessionScopeInstanceSaveFolder = AOWP_AspectInstanceManager::_getSessionScopeInstanceSaveFolder();
		$instanceSerializationFile = $sessionScopeInstanceSaveFolder . $aspectClassName;
		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 _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;
		}
	}
	
}
?>