/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;

import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;

import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;

public class InOutFlowAnalyzer extends FlowAnalyzer {

	public InOutFlowAnalyzer(FlowContext context) {
		super(context);
	}

	public FlowInfo perform(IASTNode[] selectedNodes) {
		FlowContext context= getFlowContext();
		GenericSequentialFlowInfo result= createSequential();
		for (int i= 0; i < selectedNodes.length; i++) {
			IASTNode node= selectedNodes[i];
			node.accept(this);
			result.merge(getFlowInfo(node), context);
		}
		return result;
	}

	@Override
	protected boolean traverseNode(IASTNode node) {
		// We are only traversing the selected nodes.
		return true;
	}

	@Override
	protected boolean shouldCreateReturnFlowInfo(IASTReturnStatement node) {
		// We are only traversing selected nodes.
		return true;
	}

	@Override
	public int leave(IASTCompoundStatement node) {
		super.leave(node);
		clearAccessMode(accessFlowInfo(node), node.getStatements());
		return PROCESS_SKIP;
	}

	@Override
	public int leave(ICPPASTCatchHandler node) {
		super.leave(node);
		clearAccessMode(accessFlowInfo(node), node.getDeclaration());
		return PROCESS_SKIP;
	}

	@Override
	public int leave(IASTForStatement node) {
		super.leave(node);
		clearAccessMode(accessFlowInfo(node), node.getInitializerStatement());
		return PROCESS_SKIP;
	}

	@Override
	public int leave(ICPPASTRangeBasedForStatement node) {
		super.leave(node);
		clearAccessMode(accessFlowInfo(node), node.getDeclaration());
		return PROCESS_SKIP;
	}

	@Override
	public int leave(IASTFunctionDefinition node) {
		super.leave(node);
		FlowInfo info= accessFlowInfo(node);
		IASTFunctionDeclarator declarator = node.getDeclarator();
		if (declarator instanceof IASTStandardFunctionDeclarator) {
			for (IASTParameterDeclaration param : ((IASTStandardFunctionDeclarator) declarator).getParameters()) {
				clearAccessMode(info, param.getDeclarator());
			}
		} else if (declarator instanceof ICASTKnRFunctionDeclarator) {
			for (IASTDeclaration param : ((ICASTKnRFunctionDeclarator) declarator).getParameterDeclarations()) {
				clearAccessMode(info, param);
			}
		}
		return PROCESS_SKIP;
	}

	private void clearAccessMode(FlowInfo info, IASTStatement[] statements) {
		if (statements == null || statements.length == 0 || info == null)
			return;
		for (IASTStatement statement : statements) {
			clearAccessMode(info, statement);
		}
	}

	private void clearAccessMode(FlowInfo info, IASTStatement statement) {
		if (statement instanceof IASTDeclarationStatement) {
			IASTDeclaration declaration = ((IASTDeclarationStatement) statement).getDeclaration();
			clearAccessMode(info, declaration);
		}
	}

	private void clearAccessMode(FlowInfo info, IASTDeclaration declaration) {
		if (declaration instanceof IASTSimpleDeclaration) {
			IASTDeclarator[] declarators = ((IASTSimpleDeclaration) declaration).getDeclarators();
			for (IASTDeclarator declarator : declarators) {
				clearAccessMode(info, declarator);
			}
		}
	}

	private void clearAccessMode(FlowInfo info, IASTDeclarator declarator) {
		declarator = CPPVisitor.findInnermostDeclarator(declarator);
		IASTName name = declarator.getName();
		IBinding binding= name.resolveBinding();
		if (binding instanceof IVariable && !(binding instanceof IField))
			info.clearAccessMode((IVariable) binding, fFlowContext);
	}
}

