/*******************************************************************************
 * Copyright (c) 2008, 2012 Institute for Software, HSR Hochschule fuer Technik  
 * Rapperswil, University of applied sciences 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: 
 *     Institute for Software - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.utils;

import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.viewers.ISelection;

import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;

import org.eclipse.cdt.internal.ui.refactoring.Container;

/**
 * Helper class to support operations concerning a selection.
 * 
 * @author Mirko Stocker, Lukas Felber
 */
public class SelectionHelper {

	public static Region getRegion(ISelection selection) {
		if (selection instanceof ITextSelection) {
			final ITextSelection txtSelection= (ITextSelection) selection;
			return new Region(txtSelection.getOffset(), txtSelection.getLength());
		}
		return null;
	}
	
	public static IASTSimpleDeclaration findFirstSelectedDeclaration(final Region textSelection,
			IASTTranslationUnit translationUnit) {
		final Container<IASTSimpleDeclaration> container = new Container<IASTSimpleDeclaration>();

		translationUnit.accept(new ASTVisitor() {
			{
				shouldVisitDeclarations = true;
			}
			@Override
			public int visit(IASTDeclaration declaration) {
				if (declaration instanceof IASTSimpleDeclaration &&
						doesNodeOverlapWithRegion(declaration, textSelection)) {
					container.setObject((IASTSimpleDeclaration) declaration);
				}
				return super.visit(declaration);
			}
		});

		return container.getObject();
	}
	
	public static boolean doesNodeOverlapWithRegion(IASTNode node, Region region) {
		return doRegionsOverlap(getNodeSpan(node), region);
	}
	
	public static boolean isNodeInsideRegion(IASTNode node, Region region) {
		return isRegionInside(getNodeSpan(node), region);
	}

	/**
	 * Returns true if the first region is inside the second.
	 */
	private static boolean isRegionInside(Region region1, Region region2) {
		int offset1 = region1.getOffset();
		int offset2 = region2.getOffset();
		return offset1 >= offset2 &&
				offset1 + region1.getLength() <= offset2 + region2.getLength();
	}

	/**
	 * Returns true if the two regions have at least one common point.
	 */
	private static boolean doRegionsOverlap(Region region1, Region region2) {
		int offset1 = region1.getOffset();
		int offset2 = region2.getOffset();
		return offset1 + region1.getLength() >= offset2 &&
				offset1 <= offset2 + region2.getLength();
	}

	public static boolean isNodeInsideSelection(IASTNode node, Region selection) {
		return node.isPartOfTranslationUnitFile() && isNodeInsideRegion(node, selection);
	}

	protected static Region getNodeSpan(IASTNode region) {
		int start = Integer.MAX_VALUE;
		int nodeLength = 0;
		IASTNodeLocation[] nodeLocations = region.getNodeLocations();
		if (nodeLocations.length != 1) {
			for (IASTNodeLocation location : nodeLocations) {
				if (location instanceof IASTMacroExpansionLocation) {
					IASTMacroExpansionLocation macroLoc = (IASTMacroExpansionLocation) location;
					int nodeOffset = macroLoc.asFileLocation().getNodeOffset();
					if (nodeOffset < start) {
						start = nodeOffset;
					}
					nodeLength += macroLoc.asFileLocation().getNodeLength();
				} else {
					IASTFileLocation loc = region.getFileLocation();
					int nodeOffset = loc.getNodeOffset();
					if (nodeOffset < start) {
						start = nodeOffset;
					}
					nodeLength = loc.getNodeLength();
				}
			}
		} else {
			if (nodeLocations[0] instanceof IASTMacroExpansionLocation) {
				IASTMacroExpansionLocation macroLoc = (IASTMacroExpansionLocation) nodeLocations[0];
				start = macroLoc.asFileLocation().getNodeOffset();
				nodeLength = macroLoc.asFileLocation().getNodeLength();
			} else {
				IASTFileLocation loc = region.getFileLocation();
				start = loc.getNodeOffset();
				nodeLength = loc.getNodeLength();
			}
		}
		return new Region(start, nodeLength);
	}
}
