/*******************************************************************************
 * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
 * 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: The Chisel Group, University of Victoria
 *******************************************************************************/
package org.eclipse.zest.layouts.algorithms;

import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;
import org.eclipse.zest.layouts.dataStructures.InternalNode;
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;

/**
 * 
 * @author Ian Bull
 * 
 * Used to represent algorithms that can continuously run.  
 *
 */
public abstract class ContinuousLayoutAlgorithm extends AbstractLayoutAlgorithm {

	double x, y, widht, height;

	public ContinuousLayoutAlgorithm(int styles) {
		super(styles);
	}

	/**
	 * The logic to determine if a layout should continue running or not
	 */
	protected abstract boolean performAnotherNonContinuousIteration();

	/**
	 * Computes a single iteration of the layout algorithm
	 * @return
	 */
	protected abstract void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height);

	private boolean continueRunning() {
		if (layoutStopped) {
			return false;
		} else if (this.internalContinuous && !layoutStopped) {
			return true;
		} else if (performAnotherNonContinuousIteration()) {
			return true;
		} else {
			return false;
		}
	}

	public void setLayoutArea(double x, double y, double width, double height) {
		this.setBounds(x, y, width, height);

	}

	public synchronized DisplayIndependentRectangle getBounds() {
		return new DisplayIndependentRectangle(this.x, this.y, this.widht, this.height);
	}

	public synchronized void setBounds(double x, double y, double width, double height) {
		this.x = x;
		this.y = y;
		this.widht = width;
		this.height = height;
	}

	/**
	 * Calculates and applies the positions of the given entities based on a
	 * spring layout using the given relationships.
	 */
	protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {

		this.setBounds(x, y, width, height);

		while (continueRunning()) {
			// check for entities and relationships to add or remove 
			entitiesToLayout = updateEntities(entitiesToLayout);
			relationshipsToConsider = updateRelationships(relationshipsToConsider);
			DisplayIndependentRectangle bounds = this.getBounds();
			double localX = bounds.x;
			double localY = bounds.y;
			double localWidth = bounds.width;
			double localHeight = bounds.height;

			computeOneIteration(entitiesToLayout, relationshipsToConsider, localX, localY, localWidth, localHeight);

			updateLayoutLocations(entitiesToLayout);

			if (this.internalContinuous) {
				fireProgressEvent(1, 1);
			} else {
				fireProgressEvent(getCurrentLayoutStep(), getTotalNumberOfLayoutSteps());
			}

		}
	}

}
