/*******************************************************************************
 * Copyright (c) 2000, 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.draw2d;

import java.util.Map;

import org.eclipse.swt.graphics.GC;

import org.eclipse.draw2d.geometry.Rectangle;

/**
 * Update managers handle the job of laying out and repainting figures. A
 * desirable implementation is to batches work to be done and collapses any
 * redundant work. For example, clients may be making multiple changes to
 * figures, which require laying out the same container or repainting the same
 * region.
 * <P>
 * The update manager receives requests to validate certain figures, and repaint
 * certain areas of figures. An update manager could process every request
 * synchronously, or it could batch these requests and process them
 * asynchronously.
 * <P>
 * The update process occurs in two phases. The first phase is laying out
 * invalid figures. This phase comes first because it usually introduces
 * additional damage regions. In some cases, while validating figures, new
 * invalid figures may be appended to the update manager. Of course, damage
 * regions will be reported too as figures are layed out.
 * <P>
 * The second phase is to repaint all damaged areas. The update manager will
 * typically batch, clip, and union, all rectangles and perform a single paint
 * of the overall damaged area.
 * 
 */
public abstract class UpdateManager {

	private UpdateListener listeners[] = new UpdateListener[0];
	private boolean disposed;

	/**
	 * Adds the dirty region defined by the coordinates on the IFigure
	 * <b>figure</b>. The update manager should repaint the dirty region in a
	 * timely fashion.
	 * 
	 * @param figure
	 *            the dirty figure
	 * @param x
	 *            the x coordinate of the dirty region
	 * @param y
	 *            the y coordinate of the dirty region
	 * @param w
	 *            the width of the dirty region
	 * @param h
	 *            the height of the dirty region
	 */
	public abstract void addDirtyRegion(IFigure figure, int x, int y, int w,
			int h);

	/**
	 * @see #addDirtyRegion(IFigure, int, int, int, int)
	 */
	public void addDirtyRegion(IFigure figure, Rectangle rect) {
		addDirtyRegion(figure, rect.x, rect.y, rect.width, rect.height);
	}

	/**
	 * Causes an update to occur at some time, and the given runnable to be
	 * executed following the update.
	 * 
	 * @since 3.1
	 * @param run
	 *            the runnable
	 */
	public void runWithUpdate(Runnable run) {
	}

	/**
	 * The receiver should call validate() on the IFigure <i>figure</i> in a
	 * timely fashion.
	 * 
	 * @param figure
	 *            the invalid figure
	 */
	public abstract void addInvalidFigure(IFigure figure);

	/**
	 * Adds the given listener to the list of listeners to be notified of
	 * painting and validation.
	 * 
	 * @param listener
	 *            the listener to add
	 */
	public void addUpdateListener(UpdateListener listener) {
		if (listener == null)
			throw new IllegalArgumentException();
		if (listeners == null) {
			listeners = new UpdateListener[1];
			listeners[0] = listener;
		} else {
			int oldSize = listeners.length;
			UpdateListener newListeners[] = new UpdateListener[oldSize + 1];
			System.arraycopy(listeners, 0, newListeners, 0, oldSize);
			newListeners[oldSize] = listener;
			listeners = newListeners;
		}
	}

	/**
	 * Called when the EditPartViewer is being disposed.
	 */
	public void dispose() {
		disposed = true;
	}

	/**
	 * Notifies listeners that painting is about to occur, passing them the
	 * damaged rectangle and the map of dirty regions.
	 * 
	 * @param damage
	 *            the damaged rectangle
	 * @param dirtyRegions
	 *            map of dirty regions to figures
	 */
	protected void firePainting(Rectangle damage, Map dirtyRegions) {
		UpdateListener localListeners[] = listeners;
		for (int i = 0; i < localListeners.length; i++)
			localListeners[i].notifyPainting(damage, dirtyRegions);
	}

	/**
	 * Notifies listeners that validation is about to occur.
	 */
	protected void fireValidating() {
		UpdateListener localListeners[] = listeners;
		for (int i = 0; i < localListeners.length; i++)
			localListeners[i].notifyValidating();
	}

	/**
	 * @return whether this update manager has been disposed.
	 */
	protected boolean isDisposed() {
		return disposed;
	}

	/**
	 * Forces an update to occur. Update managers will perform updates
	 * automatically, but may do so asynchronously. Calling this method forces a
	 * synchronous update.
	 */
	public abstract void performUpdate();

	void paint(GC gc) {
		performUpdate(new Rectangle(gc.getClipping()));
	}

	/**
	 * Performs an update on the given exposed rectangle.
	 * 
	 * @param exposed
	 *            the exposed rectangle
	 */
	public abstract void performUpdate(Rectangle exposed);

	/**
	 * Removes one occurrence of the given UpdateListener by identity.
	 * 
	 * @param listener
	 *            the listener to remove
	 */
	public void removeUpdateListener(UpdateListener listener) {
		if (listener == null)
			throw new IllegalArgumentException();
		for (int index = 0; index < listeners.length; index++)
			if (listeners[index] == listener) {
				int newSize = listeners.length - 1;
				UpdateListener newListeners[] = null;
				if (newSize != 0) {
					newListeners = new UpdateListener[newSize];
					System.arraycopy(listeners, 0, newListeners, 0, index);
					System.arraycopy(listeners, index + 1, newListeners, index,
							newSize - index);
				} else {
					newListeners = new UpdateListener[0];
				}
				listeners = newListeners;
				return;
			}
	}

	/**
	 * Sets the GraphicsSource for this update manager.
	 * 
	 * @param gs
	 *            the new GraphicsSource
	 */
	public abstract void setGraphicsSource(GraphicsSource gs);

	/**
	 * Sets the root figure.
	 * 
	 * @param figure
	 *            the new root figure
	 */
	public abstract void setRoot(IFigure figure);

	/**
	 * Performs a partial update if supported (validation only). Fires
	 * notification to listeners that validation has been performed. By default
	 * this method calls {@link #performUpdate()}. Subclasses should override
	 * this method to support validation without repainting.
	 * 
	 * @since 3.2
	 */
	public void performValidation() {
		performUpdate();
	}

}
