/*******************************************************************************
 * Copyright (c) 2011, 2016 Institute for Software, HSR Hochschule fuer Technik
 * Rapperswil, University of applied sciences and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Martin Schwab & Thomas Kallenberg - initial API and implementation
 *     Marc-Andre Laperle (Ericsson)
 *     Thomas Corbat (IFS)
 ******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.togglefunction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;

import org.eclipse.cdt.core.dom.ast.ASTNodeFactoryFactory;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
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.IASTNode.CopyStyle;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite.CommentPosition;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.dom.rewrite.DeclarationGeneratorImpl;
import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
import org.eclipse.cdt.ui.CUIPlugin;

public class ToggleNodeHelper extends NodeHelper {
	private static final String EMPTY_STRING = ""; //$NON-NLS-1$
	private static final ICPPNodeFactory factory = ASTNodeFactoryFactory.getDefaultCPPNodeFactory();

	private static void removeParameterInitializations(IASTFunctionDeclarator funcDecl) {
		for (IASTNode child : funcDecl.getChildren()) {
			if (child instanceof IASTParameterDeclaration) {
				IASTParameterDeclaration parameter = (IASTParameterDeclaration) child;
				parameter.getDeclarator().setInitializer(null);
			}
		}
	}

	private static List<ICPPASTConstructorChainInitializer> getInitializerList(IASTFunctionDefinition definition) {
		ArrayList<ICPPASTConstructorChainInitializer> initalizers = new ArrayList<>();

		for (IASTNode node : definition.getChildren()) {
			if (node instanceof ICPPASTConstructorChainInitializer) {
				initalizers.add(((ICPPASTConstructorChainInitializer) node).copy(CopyStyle.withLocations));
			}
		}
		return initalizers;
	}

	static IASTSimpleDeclaration createDeclarationFromDefinition(IASTFunctionDefinition oldDefinition) {
		IASTDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations);
		IASTDeclSpecifier newDeclSpec = oldDefinition.getDeclSpecifier().copy(CopyStyle.withLocations);
		IASTSimpleDeclaration newDeclaration = factory.newSimpleDeclaration(newDeclSpec);
		newDeclaration.addDeclarator(newDeclarator);
		return newDeclaration;
	}

	static ICPPASTFunctionDefinition createFunctionSignatureWithEmptyBody(IASTDeclSpecifier newDeclSpec,
			IASTFunctionDeclarator newFuncDecl, IASTFunctionDefinition oldDefinition) {
		ICPPASTFunctionDefinition newFunc = null;
		newFuncDecl = adjustParamNames(newFuncDecl, oldDefinition);
		if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) {
			newFunc = factory.newFunctionTryBlock(newDeclSpec, newFuncDecl, factory.newCompoundStatement());
		} else {
			newFunc = factory.newFunctionDefinition(newDeclSpec, newFuncDecl, factory.newCompoundStatement());
		}
		copyInitializerList(newFunc, oldDefinition);
		return newFunc;
	}

	private static IASTFunctionDeclarator adjustParamNames(IASTFunctionDeclarator newFuncDecl,
			IASTFunctionDefinition oldDefinition) {
		if (oldDefinition.getDeclarator() instanceof IASTStandardFunctionDeclarator) {
			IASTStandardFunctionDeclarator oldStdDec = (IASTStandardFunctionDeclarator) oldDefinition.getDeclarator();
			IASTParameterDeclaration[] definitionParams = oldStdDec.getParameters();
			IASTParameterDeclaration[] declarationParams = ((IASTStandardFunctionDeclarator) newFuncDecl)
					.getParameters();
			for (int i = 0; i < declarationParams.length; ++i) {
				declarationParams[i].getDeclarator()
						.setName(definitionParams[i].getDeclarator().getName().copy(CopyStyle.withLocations));
			}
		}
		return newFuncDecl;
	}

	private static void copyInitializerList(ICPPASTFunctionDefinition newFunc, IASTFunctionDefinition oldFunc) {
		for (ICPPASTConstructorChainInitializer initializer : getInitializerList(oldFunc)) {
			initializer.setParent(newFunc);
			newFunc.addMemberInitializer(initializer);
		}
	}

	static IASTFunctionDefinition getQualifiedNameDefinition(IASTFunctionDefinition oldDefinition,
			IASTTranslationUnit definitionUnit, IASTNode nameSpace) {
		ICPPASTDeclSpecifier newDeclSpecifier = createDeclSpecifier(oldDefinition);
		IASTFunctionDeclarator newDeclarator = oldDefinition.getDeclarator().copy(CopyStyle.withLocations);
		newDeclarator.setName(getQualifiedName(oldDefinition.getDeclarator(), nameSpace));
		removeParameterInitializations(newDeclarator);

		ICPPASTFunctionDefinition newFunction = createFunctionSignatureWithEmptyBody(newDeclSpecifier, newDeclarator,
				oldDefinition);
		// Virt-specifiers are only valid in the class declaration.
		if (newFunction.getDeclarator() instanceof ICPPASTFunctionDeclarator) {
			ICPPASTFunctionDeclarator functionDeclarator = (ICPPASTFunctionDeclarator) newFunction.getDeclarator();
			functionDeclarator.setVirtSpecifiers(null);
		}

		return newFunction;
	}

	private static ICPPASTDeclSpecifier createDeclSpecifier(IASTFunctionDefinition oldDefinition) {
		IASTDeclSpecifier originalDeclSpecifier = oldDefinition.getDeclSpecifier();
		ICPPASTDeclSpecifier newDeclSpecifier = (ICPPASTDeclSpecifier) originalDeclSpecifier
				.copy(CopyStyle.withLocations);
		if (newDeclSpecifier instanceof ICPPASTNamedTypeSpecifier) {
			ICPPASTNamedTypeSpecifier newNamedTypeSpecifier = (ICPPASTNamedTypeSpecifier) newDeclSpecifier;
			IASTName typename = ((ICPPASTNamedTypeSpecifier) originalDeclSpecifier).getName();
			IBinding typenameBinding = typename.resolveBinding();
			adaptTemplateQualifiers(newNamedTypeSpecifier, typenameBinding);
		}
		newDeclSpecifier.setVirtual(false);
		newDeclSpecifier.setInline(true);
		newDeclSpecifier.setStorageClass(IASTDeclSpecifier.sc_unspecified);
		return newDeclSpecifier;
	}

	private static void adaptTemplateQualifiers(ICPPASTNamedTypeSpecifier newDeclSpecifier, IBinding typenameBinding) {
		if (typenameBinding instanceof ICPPBinding) {
			try {
				String[] nameParts = ((ICPPBinding) typenameBinding).getQualifiedName();
				String qualifiedName = typenameBinding.getName();
				String[] nameQualifiers = Arrays.copyOf(nameParts, nameParts.length - 1);
				ICPPASTQualifiedName qualifiedTypeName = factory.newQualifiedName(nameQualifiers, qualifiedName);

				if (typenameBinding instanceof ICPPTemplateInstance) {
					ICPPTemplateInstance templateInstance = (ICPPTemplateInstance) typenameBinding;
					ICPPTemplateArgument[] templateArguments = templateInstance.getTemplateArguments();
					IASTName lastName = qualifiedTypeName.getLastName();
					ICPPASTTemplateId newTemplateId = createTemplateIdForArguments(lastName, templateArguments);
					qualifiedTypeName.setLastName(newTemplateId);
				}

				boolean setTypename = adaptQualifiers(typenameBinding, qualifiedTypeName);
				newDeclSpecifier.setName(qualifiedTypeName);
				newDeclSpecifier.setIsTypename(setTypename);
			} catch (DOMException e) {
				CUIPlugin.log(e);
			}
		}
	}

	private static boolean adaptQualifiers(IBinding typenameBinding, ICPPASTQualifiedName qualifiedTypeName) {
		boolean setTypename = false;
		IBinding owner = typenameBinding.getOwner();
		ICPPASTNameSpecifier[] qualifiers = qualifiedTypeName.getQualifier();
		if (qualifiers.length > 0) {
			int level = qualifiers.length - 1;
			while (owner != null && level >= 0) {
				IASTName qualifierName = (IASTName) qualifiers[level];
				ICPPASTTemplateId newTemplateId = createTemplateIdForQualifier(qualifierName, owner);
				if (newTemplateId != null) {
					qualifiers[level] = newTemplateId;
					newTemplateId.setParent(qualifiedTypeName);
					setTypename = true;
				}
				owner = owner.getOwner();
				level--;
			}
		}
		return setTypename;
	}

	private static ICPPASTTemplateId createTemplateIdForQualifier(IASTName qualifierName, IBinding templateBinding) {
		ICPPASTTemplateId newTemplateId = null;
		if (templateBinding instanceof ICPPClassTemplate) {
			ICPPClassTemplate classTemplateBinding = (ICPPClassTemplate) templateBinding;
			newTemplateId = createTemplateId(qualifierName, classTemplateBinding);
		} else if (templateBinding instanceof ICPPTemplateInstance) {
			ICPPTemplateInstance instanceBinding = (ICPPTemplateInstance) templateBinding;
			ICPPTemplateArgument[] templateArguments = instanceBinding.getTemplateArguments();
			newTemplateId = createTemplateIdForArguments(qualifierName, templateArguments);
		}
		return newTemplateId;
	}

	private static ICPPASTTemplateId createTemplateIdForArguments(IASTName qualifierName,
			ICPPTemplateArgument[] templateArguments) {
		ICPPASTTemplateId newTemplateId = factory.newTemplateId(qualifierName);
		for (ICPPTemplateArgument templateArgument : templateArguments) {
			IType type = templateArgument.getOriginalTypeValue();
			DeclarationGeneratorImpl declarationGeneratorImpl = new DeclarationGeneratorImpl(factory);
			IASTDeclarator abstractDeclarator = declarationGeneratorImpl.createDeclaratorFromType(type,
					EMPTY_STRING.toCharArray());
			IType ultimateType = SemanticUtil.getUltimateType(type, false);
			IASTName templateParameterName = factory.newName(ASTTypeUtil.getType(ultimateType, false));
			ICPPASTNamedTypeSpecifier typeSpecifier = factory.newNamedTypeSpecifier(templateParameterName);
			ICPPASTTypeId newTypeId = factory.newTypeId(typeSpecifier, abstractDeclarator);
			newTemplateId.addTemplateArgument(newTypeId);
		}
		return newTemplateId;
	}

	private static ICPPASTTemplateId createTemplateId(IASTName qualifierName, ICPPClassTemplate ownerBinding) {
		ICPPASTTemplateId newTemplateId = factory.newTemplateId(qualifierName);
		for (ICPPTemplateParameter templateParameter : ownerBinding.getTemplateParameters()) {
			IASTName abstractDeclaratorName = factory.newName();
			ICPPASTDeclarator abstractDeclarator = factory.newDeclarator(abstractDeclaratorName);
			ICPPASTName templateParameterName = factory.newName(templateParameter.getNameCharArray());
			ICPPASTNamedTypeSpecifier typeSpecifier = factory.newNamedTypeSpecifier(templateParameterName);
			ICPPASTTypeId newTypeId = factory.newTypeId(typeSpecifier, abstractDeclarator);
			newTemplateId.addTemplateArgument(newTypeId);
		}
		return newTemplateId;
	}

	public static ICPPASTTemplateDeclaration getTemplateDeclaration(IASTFunctionDefinition oldFunction,
			IASTFunctionDefinition newFunction) {
		ArrayList<ICPPASTTemplateDeclaration> templateDeclarations = getAllTemplateDeclarations(oldFunction);
		return addTemplateDeclarationsInOrder(templateDeclarations, newFunction);
	}

	private static ICPPASTTemplateDeclaration addTemplateDeclarationsInOrder(
			ArrayList<ICPPASTTemplateDeclaration> templDecs, IASTFunctionDefinition newFunction) {
		ICPPASTTemplateDeclaration child = null;
		for (ICPPASTTemplateDeclaration templateDeclaration : templDecs) {
			if (templateDeclaration.getTemplateParameters().length == 0) {
				continue;
			}
			if (child == null) {
				child = templateDeclaration;
				child.setDeclaration(newFunction);
			} else {
				templateDeclaration.setDeclaration(child);
				child.setParent(templateDeclaration);
				child = templateDeclaration;
			}
		}
		return child;
	}

	private static ArrayList<ICPPASTTemplateDeclaration> getAllTemplateDeclarations(IASTNode node) {
		ArrayList<ICPPASTTemplateDeclaration> templdecs = new ArrayList<>();
		while (node.getParent() != null) {
			node = node.getParent();
			if (node instanceof ICPPASTTemplateDeclaration) {
				templdecs.add((ICPPASTTemplateDeclaration) node.copy(CopyStyle.withLocations));
			}
		}
		return templdecs;
	}

	static IASTFunctionDefinition createInClassDefinition(IASTFunctionDeclarator dec, IASTFunctionDefinition def,
			IASTTranslationUnit insertionAst) {
		IASTFunctionDeclarator declarator = dec.copy(CopyStyle.withLocations);
		IASTDeclSpecifier declSpec = def.getDeclSpecifier().copy(CopyStyle.withLocations);
		declSpec.setInline(false);
		if (declSpec instanceof ICPPASTDeclSpecifier && ToggleNodeHelper.isVirtual(dec)) {
			((ICPPASTDeclSpecifier) declSpec).setVirtual(true);
		}
		declSpec.setStorageClass(getStorageClass(dec));

		return createFunctionSignatureWithEmptyBody(declSpec, declarator, def);
	}

	static boolean isVirtual(IASTFunctionDeclarator fdec) {
		if (fdec.getParent() instanceof IASTSimpleDeclaration) {
			IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent();
			return ((ICPPASTDeclSpecifier) dec.getDeclSpecifier()).isVirtual();
		}
		return false;
	}

	static int getStorageClass(IASTFunctionDeclarator fdec) {
		if (fdec.getParent() instanceof IASTSimpleDeclaration) {
			IASTSimpleDeclaration dec = (IASTSimpleDeclaration) fdec.getParent();
			return dec.getDeclSpecifier().getStorageClass();
		}
		return -1;
	}

	static IASTNode getParentRemovePoint(IASTFunctionDefinition definition) {
		IASTNode toremove = definition;
		while (toremove.getParent() != null && toremove.getParent() instanceof ICPPASTTemplateDeclaration) {
			toremove = toremove.getParent();
		}
		return toremove;
	}

	/**
	 * @param declarator the declarator from which the full qualified namespace should be fetched
	 * @param limiter set a limiter in the class hierarchy where the lookup will stop
	 * @return
	 */
	static ICPPASTQualifiedName getQualifiedName(IASTFunctionDeclarator declarator, IASTNode limiter) {
		Stack<IASTNode> nodes = getQualifiedNames(declarator, limiter, declarator);
		ICPPASTQualifiedName qName = reAssembleQualifiedName(nodes);
		qName.addName(declarator.getName().copy(CopyStyle.withLocations));
		return qName;
	}

	private static ICPPASTQualifiedName reAssembleQualifiedName(Stack<IASTNode> nodes) {
		ICPPASTQualifiedName qName = factory.newQualifiedName(null);
		while (!nodes.isEmpty()) {
			IASTNode nnode = nodes.pop();
			if (nnode instanceof IASTCompositeTypeSpecifier) {
				qName.addName(((IASTCompositeTypeSpecifier) nnode).getName());
			} else if (nnode instanceof ICPPASTNamespaceDefinition) {
				qName.addName(((ICPPASTNamespaceDefinition) nnode).getName());
			} else if (nnode instanceof ICPPASTTemplateId) {
				qName.addName((ICPPASTTemplateId) nnode);
			}
		}
		return qName;
	}

	private static Stack<IASTNode> getQualifiedNames(IASTFunctionDeclarator declarator, IASTNode limiter,
			IASTNode node) {
		IASTName lastName = declarator.getName();
		Stack<IASTNode> nodes = new Stack<>();
		while (node.getParent() != null && node.getParent() != limiter) {
			node = node.getParent();
			if (node instanceof IASTCompositeTypeSpecifier) {
				nodes.push(((IASTCompositeTypeSpecifier) node).copy(CopyStyle.withLocations));
				lastName = ((IASTCompositeTypeSpecifier) node).getName();
			} else if (node instanceof ICPPASTNamespaceDefinition) {
				nodes.push(((ICPPASTNamespaceDefinition) node).copy(CopyStyle.withLocations));
				lastName = ((ICPPASTNamespaceDefinition) node).getName();
			} else if (!(lastName instanceof ICPPASTTemplateId) && shouldAddTemplateBrackets(node)) {
				if (!nodes.isEmpty())
					nodes.pop();
				ICPPASTTemplateId templateID = getTemplateParameter(node, lastName);
				nodes.add(templateID);
			}
		}
		return nodes;
	}

	private static boolean shouldAddTemplateBrackets(IASTNode node) {
		return node instanceof ICPPASTTemplateDeclaration
				&& !(((ICPPASTTemplateDeclaration) node).getDeclaration() instanceof ICPPASTFunctionDefinition);
	}

	private static ICPPASTTemplateId getTemplateParameter(IASTNode node, IASTName name) {
		ICPPASTTemplateId templateID = factory.newTemplateId(name.copy(CopyStyle.withLocations));
		for (IASTNode child : node.getChildren()) {
			if (child instanceof ICPPASTSimpleTypeTemplateParameter) {
				ICPPASTSimpleTypeTemplateParameter tempChild = (ICPPASTSimpleTypeTemplateParameter) child;

				IASTName argumentName = tempChild.getName().copy(CopyStyle.withLocations);
				ICPPASTNamedTypeSpecifier namedTypeSpecifier = factory.newNamedTypeSpecifier(argumentName);

				ICPPASTTypeId id = factory.newTypeId(namedTypeSpecifier, null);
				templateID.addTemplateArgument(id);
			}
		}
		return templateID;
	}

	public static String getFilenameWithoutExtension(String filename) {
		int indexP = filename.lastIndexOf('.');
		int indexS = filename.lastIndexOf('/');
		indexS++;
		return filename.substring(indexS, indexP);
	}

	/**
	 * Will extract the innermost ICPPASTFunctionDefinition out of a template declaration.
	 *
	 * template<typename T>				// <-- input this node
	 * template<typename U>
	 * void function(T t, U u) { ... }  // <-- will find this node here
	 *
	 * @param declaration the template declaration that should be searched for the function definition.
	 * @return null if a declaration is found instead of a definition.
	 */
	public static ICPPASTFunctionDefinition getFunctionDefinition(IASTNode declaration) {
		IASTNode node = declaration;
		while (node != null) {
			if (node instanceof ICPPASTTemplateDeclaration) {
				ICPPASTTemplateDeclaration templdec = (ICPPASTTemplateDeclaration) node;
				node = templdec.getDeclaration();
				continue;
			}
			if (node instanceof ICPPASTFunctionDefinition) {
				return (ICPPASTFunctionDefinition) node;
			} else {
				return null;
			}
		}
		return null;
	}

	/**
	 * Gets comments inside the body of a function.
	 * @return The body as a string and all the catch handlers
	 */
	public static String getBody(IASTFunctionDefinition oldDefinition, IASTTranslationUnit ast,
			ModificationCollector modifications) {
		return getBodyOnly(oldDefinition, ast, modifications) + getCatchHandlers(oldDefinition, ast, modifications);
	}

	private static String getBodyOnly(IASTFunctionDefinition oldDefinition, IASTTranslationUnit ast,
			ModificationCollector modifications) {
		String leadingComments = getCommentsAsString(
				getLeadingCommentsFromNode(oldDefinition.getBody(), ast, modifications));
		String trailingComments = getCommentsAsString(getTrailingComments(oldDefinition.getBody(), ast, modifications));
		return leadingComments + oldDefinition.getBody().getRawSignature() + trailingComments;
	}

	private static String getCatchHandlers(IASTFunctionDefinition oldDefinition, IASTTranslationUnit ast,
			ModificationCollector modifications) {
		if (oldDefinition instanceof ICPPASTFunctionWithTryBlock) {
			ICPPASTCatchHandler[] oldCatches = ((ICPPASTFunctionWithTryBlock) oldDefinition).getCatchHandlers();
			String allCatchHandlers = ""; //$NON-NLS-1$
			for (int i = 0; i < oldCatches.length; i++) {
				String lead = getCommentsAsString(getLeadingCommentsFromNode(oldCatches[i], ast, modifications));
				String trail = getCommentsAsString(getTrailingComments(oldCatches[i], ast, modifications));
				allCatchHandlers += lead + oldCatches[i].getRawSignature() + trail;
			}
			return allCatchHandlers;
		}
		return ""; //$NON-NLS-1$
	}

	private static List<IASTComment> getLeadingCommentsFromNode(IASTNode existingNode, IASTTranslationUnit ast,
			ModificationCollector modifications) {
		ASTRewrite rw = modifications.rewriterForTranslationUnit(ast);
		return rw.getComments(existingNode, CommentPosition.leading);
	}

	private static List<IASTComment> getTrailingComments(IASTNode existingNode, IASTTranslationUnit ast,
			ModificationCollector modifications) {
		ASTRewrite rw = modifications.rewriterForTranslationUnit(ast);
		return rw.getComments(existingNode, CommentPosition.trailing);
	}

	public static IASTNode getParentTemplateDeclaration(IASTNode def) {
		if (def == null)
			return null;
		IASTNode lastSeen = def;
		IASTNode node = def.getParent();
		while (node != null) {
			if (node instanceof ICPPASTTemplateDeclaration || node instanceof IASTSimpleDeclaration) {
				lastSeen = node;
				node = node.getParent();
				continue;
			}
			return lastSeen;
		}
		return lastSeen;
	}

	private static String getCommentsAsString(List<IASTComment> commentList) {
		String comments = EMPTY_STRING;
		for (IASTComment c : commentList) {
			comments += c.getRawSignature() + System.getProperty("line.separator"); //$NON-NLS-1$
		}
		return comments;
	}

	/**
	 * Returns all namespace definitions surrounding <code>node</code>, ordered from outer to inner.
	 * @param node to collect the namespaces for.
	 * @return List of the surrounding namespaces.
	 */
	public static List<ICPPASTNamespaceDefinition> findSurroundingNamespaces(IASTNode node) {
		ArrayList<ICPPASTNamespaceDefinition> namespaces = new ArrayList<>();
		ICPPASTNamespaceDefinition currentNamespace = ASTQueries.findAncestorWithType(node,
				ICPPASTNamespaceDefinition.class);
		while (currentNamespace != null) {
			namespaces.add(0, currentNamespace);
			currentNamespace = ASTQueries.findAncestorWithType(currentNamespace.getParent(),
					ICPPASTNamespaceDefinition.class);
		}
		return namespaces;
	}
}
