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

/**
 * 
 * @author keiji
 * @package aowp.aspect.weaver
 */
class AOWP_ASTManager {
	const AST_NAME_SEPARTOR = '__';
	
	private $_RemainingASTTasks = array();
	
	const AST_CACHE_DIR = 'ASTCache';
	const SERIARIZE_FILENAME = 'AOWP_ASTManager';
	
	public function __construct() {
		AOWP_Logger::setStartTime();
		$this->_RemainingASTTasks = AOWP_PHPFileManager::getAllProjectPHPFile();
		AOWP_Logger::logging('[Instantiation of ASTManager] Load ' . count($this->_RemainingASTTasks) . ' file paths are loaded.');
	}
	
	public function getRemainingASTTaskNum() {
		return count($this->_RemainingASTTasks);
	}
	
	/**
	 * 解析したASTを、保存するフォルダを取得する。
	 * 
	 * @uses AOWP_ConfigurationManager::getCacheFolder()
	 * @return string
	 */
	public static function cacheDirPath() {
		$cacheDirPath = AOWP_ConfigurationManager::getCacheFolder() . AOWP_ASTManager::AST_CACHE_DIR . DIRECTORY_SEPARATOR;
		if (!file_exists($cacheDirPath)) {
			AOWP_PHPFileManager::mkdir($cacheDirPath);
		}
		return $cacheDirPath;
	}
	/**
	 * 織り込みの途中段階のASTを、保存するフォルダを取得する。
	 * 
	 * @uses AOWP_ConfigurationManager::getCacheFolder()
	 * @return string
	 */
	private static function _weavingCacheDirPath() {
		return AOWP_ASTManager::cacheDirPath() . 'weave' . DIRECTORY_SEPARATOR;
	}
	private static function _getASTCacheName($filePathOrASTFileName) {
		$projectPathPos = strpos($filePathOrASTFileName, AOWP_ConfigurationManager::getProjectHomePath());
		if ($projectPathPos !== false) {
			$relativePath = substr($filePathOrASTFileName, $projectPathPos + strlen(AOWP_ConfigurationManager::getProjectHomePath()));
			return str_replace(DIRECTORY_SEPARATOR, AOWP_ASTManager::AST_NAME_SEPARTOR, $relativePath) . '.ast';
		}
		else {
			return $filePathOrASTFileName;
		}
	}
	private static function _getSourceRelativePath($astFileName) {
		$sourcePath = substr($astFileName, 0, strlen($astFileName) - strlen('.ast'));
		return str_replace(AOWP_ASTManager::AST_NAME_SEPARTOR, DIRECTORY_SEPARATOR, $sourcePath);
	}
	public static function getSourcePath($astFileName) {
		return AOWP_ConfigurationManager::getProjectHomePath() . AOWP_ASTManager::_getSourceRelativePath($astFileName);
	}
	private static function _getWovenASTPath($filePathOrASTFileName) {
		return AOWP_ASTManager::_weavingCacheDirPath() . AOWP_ASTManager::_getASTCacheName($filePathOrASTFileName);
	}
	private static function _getASTPath($filePathOrASTFileName) {
		return AOWP_ASTManager::cacheDirPath() . AOWP_ASTManager::_getASTCacheName($filePathOrASTFileName);
	}
	
	public static function saveCache($fileName, &$ast, $cacheDir) {
		if (!file_exists($cacheDir)) {
			AOWP_PHPFileManager::mkdir($cacheDir);
		}
		$savedFileName = preg_match('/.ast$/', $fileName) ? $fileName : AOWP_ASTManager::_getASTCacheName($fileName); 
		$astCacheContent = serialize($ast);
		AOWP_PHPFileManager::saveFile($cacheDir . DIRECTORY_SEPARATOR . $savedFileName, $astCacheContent);
	}
	public static function saveWeaveCache($fileName, &$ast) {
		AOWP_ASTManager::saveCache($fileName, $ast, AOWP_ASTManager::_weavingCacheDirPath());
	}
	
	
	public static function getAllCachedASTFileName() {
		$astPathArray = array();
		$astDirectory = dir(AOWP_ASTManager::cacheDirPath());
		while (($file = $astDirectory->read()) !== false) {
			if (!is_dir($file) && preg_match('/.ast$/', $file)) {
				$astPathArray[] = $file;
			}
		}
		return $astPathArray;
	}
	
	public static function &parsePHPFile($fileName) {
		$cacheDir = AOWP_ASTManager::cacheDirPath();
		if (!file_exists($cacheDir)) {
			AOWP_PHPFileManager::mkdir($cacheDir);
		}	
		$cacheFilePath = AOWP_ASTManager::_getASTPath($fileName);
		$ast = null;
		AOWP_Logger::setStartTime();
		if (file_exists($cacheFilePath)) {
			$cacheFileContent = file_get_contents($cacheFilePath);
			$ast = unserialize($cacheFileContent);
			AOWP_Logger::logging('[Use AST cache] ' . $fileName);
		}
		else {
			$ast = &PHP_Parser::staticParseFile($fileName);
			AOWP_ASTManager::saveCache($fileName, $ast, $cacheDir);
			AOWP_Logger::logging('[Parse PHP file] ' . $fileName);
		}
		return $ast;
	}
	
	/**
	 * 
	 * @param $astFileName
	 * @return PHP_Parser_Core
	 */
	public static function &getWovenASTFile($astFileName) {
		$wovenASTPath = AOWP_ASTManager::_getWovenASTPath($astFileName);
		if (file_exists($wovenASTPath)) {
			$unserializedInstance = unserialize(file_get_contents($wovenASTPath));
			return $unserializedInstance;
		}
		else {
			return AOWP_ASTManager::parsePHPFile($astFileName);
		}
	}
	public static function refleshWovenAST() {
		AOWP_PHPFileManager::removeFolder(AOWP_ASTManager::_weavingCacheDirPath());
		AOWP_PHPFileManager::mkdir(AOWP_ASTManager::_weavingCacheDirPath());
	}
	
	public function createAllASTCache() {
		if (file_exists(AOWP_ASTManager::cacheDirPath())) {
			AOWP_PHPFileManager::removeFolder(AOWP_ASTManager::cacheDirPath());
			AOWP_Logger::logging('[Create AST cache] Reflesh cache directory.');
		}
		while(!$this->isEndCreateCache()) {
			$this->createOneASTCache();
		}
	}
	public function createOneASTCache(){
		$filepath = array_shift($this->_RemainingASTTasks);
		$ast = &AOWP_ASTManager::parsePHPFile($filepath);
		$ast->root->releaseInstance();
	}
	
	public function isEndCreateCache(){
		return $this->_RemainingASTTasks === array();
	}
	
	
	public function cleanASTCache(){
		$cacheDir = AOWP_ConfigurationManager::getRuntimeDataFolder() . DIRECTORY_SEPARATOR . AOWP_ASTManager::AST_CACHE_DIR;
		if(file_exists($cacheDir)){
			$directory = dir($cacheDir);
			while (($fileName = $directory->read()) !== false) {
				if(is_file($fileName) && preg_match('/\.ast$/', $fileName)){
					unlink($fileName);
				}
			}
		}
	}
	
	public function getSerializeFilePath(){
		return AOWP_ConfigurationManager::getCacheFolder() . DIRECTORY_SEPARATOR . AOWP_ASTManager::SERIARIZE_FILENAME;
	}
	
	public function serialize(){
		$serializeFilePath = $this->getSerializeFilePath();
		$seriarizeContent = serialize($this);
		AOWP_PHPFileManager::saveFile($serializeFilePath, $seriarizeContent);
	}
	
	public static function generateWovenSource() {
		AOWP_PHPFileManager::backupOriginalPHPFile();
		if (!file_exists(AOWP_ConfigurationManager::getProjectHomePath())) {
			AOWP_PHPFileManager::mkdir(AOWP_ConfigurationManager::getProjectHomePath());
		}
		$allASTFileNameArray = AOWP_ASTManager::getAllCachedASTFileName();
//		$allASTFileNameArray = array('__data__class__SC_PageNavi.php.ast', '__data__class__pages__products__LC_Page_Products_Detail.php.ast');
//		$allASTFileNameArray = array('__data__class__SC_PageNavi.php.ast');
		foreach ($allASTFileNameArray as $astFileName) {
			AOWP_Logger::setStartTime();
			$ast = AOWP_ASTManager::getWovenASTFile($astFileName);
			$sourcePath = AOWP_ASTManager::getSourcePath($astFileName);
			if (!file_exists(dirname($sourcePath))) {
				AOWP_PHPFileManager::mkdir(dirname($sourcePath));
			}
//			print_r($ast->root);
//			exit();
			if (AOWP_PHPFileManager::saveFile($sourcePath, AOWP_TemplateEngine::scriptToSource($ast->root))) {
				AOWP_Logger::logging('[Woven AST to woven source] Save source ' . $sourcePath);
			}
			else {
				AOWP_Logger::logging('[Woven AST to woven source] Fail in conversion to source ' . $sourcePath);
				exit();
			}
			$ast->root->releaseInstance();
		}
	}
}

?>