content: package edu.stanford.rsl.conrad.rendering;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  
  import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
  import edu.stanford.rsl.conrad.physics.PhysicalObject;
  
  public class PrioritizableScene extends AbstractScene {
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = -8239645378207352898L;
  	public static final boolean ADD_HIGHEST_PRIORITY = true;
  	public static final boolean ADD_LOWEST_PRIORITY = false;
  
  	private ArrayList<PhysicalObject> objects = new ArrayList<PhysicalObject>();
  	private HashMap<PhysicalObject, Integer> priorityMap = new HashMap<PhysicalObject, Integer>();
  	private boolean dirty = true;
  	private int highest = 0;
  	private int lowest = 0;
  
  	public int getPriority(PhysicalObject o){
  		//System.out.println(o.getNameString());
  		return priorityMap.get(o).intValue();
  	}
  
  	/**
  	 * updates the values for the priority limits
  	 */
  	private void updateLimits(){
  		Collection<Integer> values = priorityMap.values();
  		Integer [] array = new Integer[values.size()];
  		if (array.length > 1){
  			array = values.toArray(array);
  			Arrays.sort(array);
  			highest = array[array.length-1];
  			lowest = array[0];
  		}
  		dirty = false;
  	}
  	
  	/**
  	 * Method is called after the collection changed, such that the minimum 
  	 * and maximum points of the overall scene are updated. 
  	 */
  	protected void updateSceneLimits(){
  		for (PhysicalObject e : objects)
  		{
  			if (this.max == null)
  				this.max = new PointND(e.getShape().getMax());	
  			if (this.min == null)
  				this.min = new PointND(e.getShape().getMin());
  			
  			for (int i = 0; i < this.max.getDimension(); ++i){
  				if (e.getShape().getMax().get(i) > this.max.get(i))
  					this.max.set(i, e.getShape().getMax().get(i));
  				if (e.getShape().getMin().get(i) < this.min.get(i))
  					this.min.set(i, e.getShape().getMin().get(i));
  			}
  		}
  	}
  
  	public int getHighestPriority(){
  		if (dirty){
  			updateLimits();
  		}
  		return highest;
  	}
  
  	public int getLowestPriority(){
  		if(dirty){
  			updateLimits();
  		}
  		return lowest;
  	}
  
  	public boolean add(PhysicalObject e) {
  		return add(e, ADD_HIGHEST_PRIORITY);
  	}
  
  	public boolean add(PhysicalObject e, boolean addMode){
  		Integer priority;
  		if (addMode == ADD_HIGHEST_PRIORITY){
  			priority = new Integer(getHighestPriority() + 1);
  		} else {
  			priority = new Integer(getLowestPriority() - 1);
  		}
  		return add(e,priority);
  	}
  	
  	/**
  	 * This method is always called if an object is added to the scene.
  	 * A call to this method updates the scene's limits (min, max).
  	 * 
  	 * @param e The object to add
  	 * @param priority The object's priority
  	 * @return true if the collection changed due to this call
  	 */
  	public boolean add(PhysicalObject e, int priority){
  		boolean revan = objects.add(e);
  		priorityMap.put(e, priority);
  		dirty = true;
  		updateSceneLimits();
  		return revan;
  	}
  
  	public boolean addAll(Collection<? extends PhysicalObject> c) {
  		boolean revan = true;
  		for(PhysicalObject e:c){
  			if (!add(e)){
  				revan = false;
  			}
  		}
  		return revan;
  	}
  	
  	public boolean addAll(PrioritizableScene c) {
  		boolean revan = true;
  		for(PhysicalObject e:c){
  			if (!add(e, c.getPriority(e))){
  				revan = false;
  			}
  		}
  		return revan;
  	}
  
  	public void clear() {
  		objects.clear();
  		priorityMap.clear();
  		this.max = null;
  		this.min = null;
  	}
  	
  	public void clearObjectsOnly() {
  		objects.clear();
  		priorityMap.clear();
  	}
  
  	public boolean contains(Object o) {
  		return objects.contains(o);
  	}
  
  	public boolean containsAll(Collection<?> c) {
  		return objects.containsAll(c);
  	}
  
  	public boolean isEmpty() {
  		return objects.isEmpty();
  	}
  
  	public Iterator<PhysicalObject> iterator() {
  		return objects.iterator();
  	}
  	
  	public PhysicalObject getObject(int i) {
  		if (i < objects.size() && i >= 0){
  			return objects.get(i);
  		}
  		return null;
  	}
  
  	public boolean remove(Object o) {
  		boolean revan = objects.remove(o);
  		priorityMap.remove(o);
  		dirty = true;
  		updateSceneLimits();
  		return revan;
  	}
  
  	public boolean removeAll(Collection<?> c) {
  		boolean revan = true;
  		for (Object e : c){
  			if (!remove(e)){
  				revan = false;
  			}
  		}
  		updateSceneLimits();
  		return revan;
  	}
  
  	public boolean retainAll(Collection<?> c) {
  		boolean revan = true;
  		for(Object o: this){
  			if (!c.contains(o)) {
  				if(!remove(o)){
  					revan = false;
  				}
  			}
  		}
  		updateSceneLimits();
  		return revan;
  	}
  
  	public int size() {
  		return objects.size();
  	}
  
  	public Object[] toArray() {
  		return objects.toArray();
  	}
  
  	public <T> T[] toArray(T[] a) {
  		return objects.toArray(a);
  	}
  
  }
  /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */