<?php
class AOWP_AdviceBodyConverterForScriptExecutionWeaving {
	/*
	 * 		$proceedLabel = AOWP_WeavingASTHelper::getRandomName("proceed", false);
		$proceedReturnBaseLabel = AOWP_WeavingASTHelper::getRandomName("proceedReturn", false) . '_';
		$proceedReturnFlagVariableName = AOWP_WeavingASTHelper::getRandomName('proceedReturnFlag');

	 */
	private $_convertedAdviceBodyElements;
	private $_adviceMethodElement;
	private $_proceedReturnLabels;
	private $_proceedLabel;
	private $_proceedEndLabel;
	private $_proceedReturnBaseLabel;
	private $_proceedReturnFlagVariableName;
	private $_aspectInstanceName;
	private $_joinPointInstanceName;
	
	public function __construct(AOWP_Advice $advice, $proceedLabel, $proceedReturnBaseLabel, $proceedReturnFlagVariableName, $aspectInstanceName, $joinPointInstanceName, $proceedEndLabel) {
		$this->_convertedAdviceBodyElements = array();
		$this->_adviceMethodElement = AOWP_AspectASTUtil::getAdviceMethodElement($advice);
		$this->_proceedReturnLabels = array();
		$this->_proceedLabel = $proceedLabel;
		$this->_proceedReturnBaseLabel = $proceedReturnBaseLabel;
		$this->_proceedReturnFlagVariableName = $proceedReturnFlagVariableName;
		$this->_aspectInstanceName = $aspectInstanceName;
		$this->_joinPointInstanceName = $joinPointInstanceName;
		$this->_proceedEndLabel = $proceedEndLabel;
		
		$this->_convertAdviceBody();
	}
	
	public function getConvertedAdviceBodyElements() {
		return $this->_convertedAdviceBodyElements;
	}
	public function getProceedReturnLabels() {
		return $this->_proceedReturnLabels;
	}
	
	private function _convertAdviceBody() {
		$this->_convertedAdviceBodyElements = $this->_convertInnerStatementsContainer($this->_adviceMethodElement);
	}
	private function _convertInnerStatementsContainer(AOWP_PHPInnerStatementContainerElement $innerContainerStatement) {
		$convertedElements = array();
		$innerStatementElements = $innerContainerStatement->getInnerStatements();
		foreach ($innerStatementElements as $innerStatementElement) {
			if ($innerStatementElement instanceof AOWP_PHPStatementElement) {
				$convertedElements = array_merge($convertedElements, $this->_convertStatement($innerStatementElement));
			}
			else if ($innerStatementElement instanceof AOWP_PHPIfStatementElement) {
				$convertedElements[] = $this->_convertIfStatement($innerStatementElement);
			}
			else {
				$convertedElements[] = $innerStatementElement;
			}
		}
		// Add an element for skipping original statements.
		$convertedElements[] = new AOWP_PHPStatementElement(new AOWP_PHPGotoElement($this->_proceedEndLabel));
		return $convertedElements;
	}
	private function _convertStatement(AOWP_PHPStatementElement $statement) {
		if ($this->_isProceedCall($statement->expr)) {
			return $this->_getProceedCallElements();
		}
		else {
			$this->_convertAdviceVariableName($statement);
			return array($statement);
		}
	}
	private function _convertIfStatement(AOWP_PHPIfStatementElement $ifStatement) {
		foreach ($ifStatement->innerStatements as $ifInnerStatement) {
			$ifStatement->innerStatements = array(new AOWP_PHPInnerStatementElement($this->_convertInnerStatementsContainer($ifInnerStatement)));
		}
		$convertedElseIfStatements = array();
		foreach ($ifStatement->elseifStatements as $elseIfStatement) {
			$ifStatement[] = new AOWP_PHPInnerStatementElement($this->_convertInnerStatementsContainer($elseIfStatement));
		}
		$ifStatement->elseifStatements = $convertedElseIfStatements;
		foreach ($ifStatement->elseStatements as $elseStatement) {
			$ifStatement->elseStatements = array($this->_convertInnerStatementsContainer($elseStatement));
		}
		return $ifStatement;
	}
	private function _convertAdviceVariableName(AOWP_PHPElement &$element) {
		if ($element instanceof AOWP_PHPObjectOperatorElement) {
			if ($element->getLeftVariableName() == '$this') {
				$element->setLeftVariableName($this->_aspectInstanceName);
			}
			else if ($element->getLeftVariableName() == $this->_adviceMethodElement->getParameterName(0)) {
				$element->setLeftVariableName($this->_joinPointInstanceName);
			}
		}
		foreach ($element->getChildren() as $childElement) {
			$this->_convertAdviceVariableName($childElement);
		}
	}
	private function _isProceedCall(AOWP_PHPElement $element) {
		if ($element instanceof AOWP_PHPObjectOperatorElement) {
			$firstObjectProperty = $element->getFirstObjectProperty();
			return $firstObjectProperty instanceof AOWP_PHPObjectPropertyElement &&
				$firstObjectProperty->getPropertyName() == 'proceed';
		}
		else {
			return false;
		}
	}
	private function _getProceedCallElements() {
		$nowProceedLabelText = $this->_proceedReturnBaseLabel . (count($this->_proceedReturnLabels) + 1);
		$gotoAndLabelingElements = array();
		$gotoAndLabelingElements[] = new AOWP_PHPStatementElement(new AOWP_PHPEqualExprElement($this->_proceedReturnFlagVariableName, new AOWP_PHPScalarExprElement("'" . $nowProceedLabelText . "'")));
		$gotoAndLabelingElements[] = new AOWP_PHPStatementElement(new AOWP_PHPGotoElement($this->_proceedLabel));
		$gotoAndLabelingElements[] = new AOWP_PHPLabelElement($nowProceedLabelText);
		$this->_proceedReturnLabels[] = $nowProceedLabelText;
		return $gotoAndLabelingElements;
	}
	
}