<?php
class AOWP_AspectASTUtil {
	
	private static $_PROCEED_BASE_LABEL_INDEX;
	private static $_JOIN_POINT_INSTANCE_NAME;
	private static $_PROCEED_RETURN_LABELS;
	
	public static function getAdviceMethodElement(AOWP_Advice $advice) {
		$aspectClassParseResult = PHP_Parser::staticParseFile(AOWP_AspectManager::getAspectFilePathAtWeaving($advice->getClassNameOfAspect()));
		$aspectClassElement = AOWP_AspectASTUtil::getAspectClassElement($aspectClassParseResult);
		return $aspectClassElement->getMethodAST($advice->getAdviceBodyFunctionName());
	}
	
	public static function getAspectClassElement($phpElement) {
//		echo get_class($phpElement) . "\n";
		$aspectClassElement = null;
		if ($phpElement instanceof PHP_Parser_Core) {
			foreach ($phpElement->classes as $innerElement) {
				if ($innerElement != null) {
					$aspectClassElement = AOWP_AspectASTUtil::getAspectClassElement($innerElement);
					if ($aspectClassElement != null) {
						return $aspectClassElement;
					}
				}
			}
		}
		else if ($phpElement instanceof AOWP_PHPClassElement) {
			$parentClassName = $phpElement->getExtendedClassName();
			if ($parentClassName != null &&
				is_subclass_of(new $parentClassName(), 'AOWP_Aspect')) {
				return $phpElement;
			}
		}
		else {
			$childElements = $phpElement->getChildren();
			foreach ($childElements as $childElement) {
				$aspectClassElement = AOWP_AspectASTUtil::getAspectClassElement($childElement);
				if ($aspectClassElement != null) {
					return $aspectClassElement;
				}
			}
		}
		return null;
	}

	/**
	 * For around advice for script execution join points.<br/>
	 * We must call methods for weaving into script execution join points in the following order, <br/>
	 * 1. AOWP_AspectASTUtil::replaceProceedCall <br/>
	 * 2. AOWP_AspectASTUtil::replaceJoinPointInstanceVariable <br/>
	 * 3. AOWP_AspectASTUtil::replaceThisVariale
	 * 
	 * @param AOWP_PHPMethodElement &$element
	 * @param string $aspectInstanceName
	 */
	public static function replaceThisVariable(AOWP_PHPElement &$element, $aspectInstanceName) {
		if ($element instanceof AOWP_PHPMethodElement) {
			foreach ($element->innerStatements as &$innerStatement) {
				AOWP_AspectASTUtil::replaceThisVariable($innerStatement, $aspectInstanceName);
			}
		}
		else if ($element instanceof AOWP_PHPObjectOperatorElement) {
			if ($element->getLeftVariableName() == '$this') {
				$element->setLeftVariableName($aspectInstanceName);
			}
		}
		else {
			$childElements = $element->getChildren();
			foreach ($childElements as &$childElement) {
				AOWP_AspectASTUtil::replaceThisVariable($childElement, $aspectInstanceName);
			}
		}
	}
	
	/**
	 * For around advice for script execution join points.<br/>
	 * We must call methods for weaving into script execution join points in the following order, <br/>
	 * 1. AOWP_AspectASTUtil::replaceProceedCall <br/>
	 * 2. AOWP_AspectASTUtil::replaceJoinPointInstanceVariable <br/>
	 * 3. AOWP_AspectASTUtil::replaceThisVariale
	 * 
	 * @param AOWP_PHPMethodElement $element
	 * @param unknown_type $proceedLabelText
	 * @param unknown_type $baseLabelText
	 * @param unknown_type $flagVariableName
	 */
	public static function replaceProceedCall(AOWP_PHPElement $element, $proceedLabelText, $baseLabelText, $flagVariableName) {
		echo "Start replace proceed call: " . get_class($element) . "\n";
		if ($element instanceof AOWP_PHPMethodElement) {
			AOWP_AspectASTUtil::$_JOIN_POINT_INSTANCE_NAME = $element->getParameterName(0);
			AOWP_AspectASTUtil::$_PROCEED_BASE_LABEL_INDEX = 0;
			AOWP_AspectASTUtil::$_PROCEED_RETURN_LABELS = array();
			foreach ($element->innerStatements as $innerStatement) {
				AOWP_AspectASTUtil::replaceProceedCall($innerStatement, $proceedLabelText, $baseLabelText, $flagVariableName);
			}
			return AOWP_AspectASTUtil::$_PROCEED_RETURN_LABELS;
		}
		else if ($element instanceof AOWP_PHPObjectOperatorElement) {
			if ($element->getLeftVariableName() == AOWP_AspectASTUtil::$_JOIN_POINT_INSTANCE_NAME) {
				// Replace proceed method call to statements with goto and labeling.
				$nowProceedLabelText = $baseLabelText . AOWP_AspectASTUtil::$_PROCEED_BASE_LABEL_INDEX;
				$gotoAndLabelingElements = array();
				$gotoAndLabelingElements[] = new AOWP_PHPStatementElement(new AOWP_PHPEqualExprElement($flagVariableName, new AOWP_PHPScalarExprElement($nowProceedLabelText)));
				$gotoAndLabelingElements[] = new AOWP_PHPStatementElement(new AOWP_PHPGotoElement($proceedLabelText));
				$gotoAndLabelingElements[] = new AOWP_PHPLabelElement($nowProceedLabelText);
				$parentContainerElement = &$element->getParentContainer();
				$parentContainerElement->replaceElementWithElements($gotoAndLabelingElements, $element);
				AOWP_AspectASTUtil::$_PROCEED_BASE_LABEL_INDEX++;
				AOWP_AspectASTUtil::$_PROCEED_RETURN_LABELS[] = $nowProceedLabelText;
			}
			return;
		}
		else {
			$childElements = $element->getChildren();
			foreach ($childElements as $childElement) {
				AOWP_AspectASTUtil::replaceProceedCall($childElement, $proceedLabelText, $baseLabelText, $flagVariableName);
			}
			return;
		}
	}

	/**
	 * For around advice for script execution join points.<br/>
	 * We must call methods for weaving into script execution join points in the following order, <br/>
	 * 1. AOWP_AspectASTUtil::replaceProceedCall <br/>
	 * 2. AOWP_AspectASTUtil::replaceJoinPointInstanceVariable <br/>
	 * 3. AOWP_AspectASTUtil::replaceThisVariale
	 * 
	 * @param AOWP_PHPMethodElement $element
	 * @param unknown_type $joinPointInstanceName
	 */
	public static function replaceJoinPointInstanceVariable(AOWP_PHPElement &$element, $joinPointInstanceName) {
		if ($element instanceof AOWP_PHPMethodElement) {
			AOWP_AspectASTUtil::$_JOIN_POINT_INSTANCE_NAME = $element->getParameterName(0);
			foreach ($element->innerStatements as &$innerStatement) {
				AOWP_AspectASTUtil::replaceJoinPointInstanceVariable($innerStatement, $joinPointInstanceName);
			}
		}
		else if ($element instanceof AOWP_PHPObjectOperatorElement) {
			if ($element->getLeftVariableName() == AOWP_AspectASTUtil::$_JOIN_POINT_INSTANCE_NAME) {
				$element->setLeftVariableName($joinPointInstanceName);
			}
		}
		else {
			$childElements = $element->getChildren();
			foreach ($childElements as &$childElement) {
				AOWP_AspectASTUtil::replaceJoinPointInstanceVariable($childElement, $joinPointInstanceName);
			}
		}
	}
}
?>