/*******************************************************************************
 * Copyright (c) 2010 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:
 *     Research Group Software Construction,
 *     RWTH Aachen University, Germany - initial API and implementation
 */
package org.eclipse.draw2d;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Utility class to support working with {@link Viewport}s.
 * 
 * @author Philip Ritzkopf
 * @author Alexander Nyssen
 * 
 * @since 3.6
 * 
 */
public final class ViewportUtilities {

	private ViewportUtilities() {
		// provides only static utility functions and should not be accessed in
		// any other way than static.
	}

	/**
	 * Returns all enclosing {@link Viewport}s for a given {@link IFigure},
	 * beginning with its direct enclosing {@link Viewport} up the root
	 * {@link Viewport} in the figure's parent hierarchy.
	 * 
	 * @param figure
	 * @return A list of {@link Viewport}s representing the figure's enclosing
	 *         {@link Viewport} path, where the nearest enclosing
	 *         {@link Viewport} as the first element and the root
	 *         {@link Viewport} as the last element. In case there is no
	 *         enclosing {@link Viewport}, an empty list is returned.
	 */
	public static List getEnclosingViewportsPath(IFigure figure) {
		Viewport nearestEnclosingViewport = getNearestEnclosingViewport(figure);
		if (nearestEnclosingViewport == null) {
			return new ArrayList();
		}
		Viewport rootViewport = getRootViewport(figure);
		return getViewportsPath(nearestEnclosingViewport, rootViewport, true);
	}

	/**
	 * Returns a list containing the provided leaf {@link Viewport} as the first
	 * element, and all its enclosing {@link Viewport}s up to the root
	 * {@link Viewport}, where the root {@link Viewport} forms the last element
	 * of the list.
	 * 
	 * @param leafViewport
	 *            The {@link Viewport}, whose parent hierarchy is processed.
	 * @param rootViewport
	 *            an ancestor of the given leafViewport, which marks the end
	 *            point of the hierarchy to be processed.
	 * @return A list of {@link Viewport}s containing the leaf {@link Viewport}
	 *         as the first element, the root {@link Viewport} as the last and
	 *         in between all enclosing {@link Viewport}s of the leaf
	 *         {@link Viewport} up to the root. Returns an empty list in case
	 *         leaf or root {@link Viewport} are null or in case the root
	 *         viewport is not an ancestor of the leaf {@link Viewport}.
	 */
	public static List getViewportsPath(final Viewport leafViewport,
			final Viewport rootViewport) {
		return getViewportsPath(leafViewport, rootViewport, true);
	}

	/**
	 * Returns a list containing the provided leaf {@link Viewport} as the first
	 * element, and all its enclosing {@link Viewport}s up to the root
	 * {@link Viewport}. The root {@link Viewport} forms the last element of the
	 * list, in case includeRootViewport is set to true, otherwise the viewport
	 * directly nested below the root viewport will be the last in the list.
	 * 
	 * @param leafViewport
	 *            The {@link Viewport}, whose parent hierarchy is processed.
	 * @param rootViewport
	 *            an ancestor of the given leafViewport, which marks the end
	 *            point of the hierarchy to be processed.
	 * @param includeRootViewport
	 *            whether the provided rootViewport should be included in the
	 *            list of returned viewports (as the last one) or not.
	 * @return A list of {@link Viewport}s containing the leaf {@link Viewport}
	 *         as the first element, the root {@link Viewport} as the last and
	 *         in between all enclosing {@link Viewport}s of the leaf
	 *         {@link Viewport} up to the root. Returns an empty list in case
	 *         leaf or root {@link Viewport} are null or in case the root
	 *         viewport is not an ancestor of the leaf {@link Viewport}.
	 */
	public static List getViewportsPath(final Viewport leafViewport,
			final Viewport rootViewport, boolean includeRootViewport) {
		if (leafViewport == null || rootViewport == null) {
			return Collections.EMPTY_LIST;
		}

		// search all enclosing viewports of leaf viewport up to root viewport
		// (or until no enclosing viewport can be found)
		List nestedViewports = new ArrayList();
		Viewport currentViewport = leafViewport;
		do {
			nestedViewports.add(currentViewport);
			currentViewport = ViewportUtilities
					.getNearestEnclosingViewport(currentViewport);
		} while (currentViewport != null && currentViewport != rootViewport);

		// check if root viewport is an ancestor of the given leaf viewport
		if (currentViewport != null) {
			if (includeRootViewport) {
				nestedViewports.add(currentViewport);
			}
			return nestedViewports;
		}
		return Collections.EMPTY_LIST;
	}

	/**
	 * Returns the nearest common enclosing {@link Viewport} for two given
	 * {@link Figure}s.
	 * 
	 * @param firstFigure
	 * @param secondFigure
	 * @return The nearest common {@link Viewport} of the two given figures, or
	 *         null if no common enclosing {@link Viewport} could be found.
	 */
	public static Viewport getNearestCommonViewport(IFigure firstFigure,
			IFigure secondFigure) {
		return getNearestViewport(FigureUtilities.findCommonAncestor(
				firstFigure, secondFigure));
	}

	/**
	 * Returns the upper most enclosing {@link Viewport} for the given
	 * {@link IFigure}.
	 * 
	 * @param figure
	 * @return The upper most enclosing {@link Viewport} or null if there is no
	 *         enclosing {@link Viewport} for the given {@link IFigure},
	 */
	public static Viewport getRootViewport(final IFigure figure) {
		Viewport currentViewport = getNearestViewport(figure);
		while (getNearestEnclosingViewport(currentViewport) != null) {
			currentViewport = getNearestEnclosingViewport(currentViewport);
		}
		return currentViewport;
	}

	/**
	 * Returns the given figure in case it is a {@link Viewport} itself,
	 * otherwise its nearest enclosing {@link Viewport}.
	 * 
	 * @param figure
	 * @return The given figure in case it is a {@link Viewport} itself,
	 *         otherwise the nearest enclosing {@link Viewport} or null if there
	 *         is no nearest enclosing {@link Viewport}.
	 */
	public static Viewport getNearestViewport(final IFigure figure) {
		if (figure == null) {
			return null;
		}
		if (figure instanceof Viewport) {
			return (Viewport) figure;
		} else {
			return getNearestEnclosingViewport(figure);
		}
	}

	/**
	 * Returns the nearest enclosing {@link Viewport} of a given {@link IFigure}
	 * by walking up the figure's hierarchy.
	 * 
	 * @param figure
	 * @return The nearest enclosing {@link Viewport} of the given figure, or
	 *         null if none could be found.
	 */
	public static Viewport getNearestEnclosingViewport(final IFigure figure) {
		if (figure == null) {
			return null;
		}
		Viewport viewport = null;
		IFigure currentFigure = figure;
		while (currentFigure.getParent() != null) {
			if (currentFigure.getParent() instanceof Viewport) {
				viewport = (Viewport) currentFigure.getParent();
				break;
			}
			currentFigure = currentFigure.getParent();
		}
		return viewport;
	}
}