/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.draw.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import com.cburch.draw.model.CanvasModel;
import com.cburch.draw.model.CanvasObject;

public class ZOrder {
	private ZOrder() { }
	
	public static int getZIndex(CanvasObject query, CanvasModel model) {
		// returns 0 for bottommost element, large number for topmost
		return getIndex(query, model.getObjectsFromBottom());
	}
	
	public static Map<CanvasObject, Integer> getZIndex(
			Collection<? extends CanvasObject> query, CanvasModel model) {
		// returns 0 for bottommost element, large number for topmost, ordered
		// from the bottom up.
		if (query == null) return Collections.emptyMap();
		
		Set<? extends CanvasObject> querySet = toSet(query);
		Map<CanvasObject, Integer> ret;
		ret = new LinkedHashMap<CanvasObject, Integer>(query.size());
		int z = -1;
		for (CanvasObject o : model.getObjectsFromBottom()) {
			z++;
			if (querySet.contains(o)) {
				ret.put(o, Integer.valueOf(z));
			}
		}
		return ret;
	}
	
	public static <E extends CanvasObject> List<E> sortTopFirst(
			Collection<E> objects, CanvasModel model) {
		return sortXFirst(objects, model, model.getObjectsFromBottom());
	}
	
	public static <E extends CanvasObject> List<E> sortBottomFirst(
			Collection<E> objects, CanvasModel model) {
		return sortXFirst(objects, model, model.getObjectsFromTop());
	}
	
	private static <E extends CanvasObject> List<E> sortXFirst(
			Collection<E> objects, CanvasModel model, Collection<CanvasObject> objs) {
		Set<E> set = toSet(objects);
		ArrayList<E> ret = new ArrayList<E>(objects.size());
		for (CanvasObject o : objs) {
			if (set.contains(o)) {
				@SuppressWarnings("unchecked")
				E toAdd = (E) o;
				ret.add(toAdd);
			}
		}
		return ret;
	}
	
	private static <E> Set<E> toSet(Collection<E> objects) {
		if (objects instanceof Set) {
			return (Set<E>) objects;
		} else {
			return new HashSet<E>(objects);
		}
	}
	
	// returns first object above query in the z-order that overlaps query
	public static CanvasObject getObjectAbove(CanvasObject query,
			CanvasModel model, Collection<? extends CanvasObject> ignore) {
		return getPrevious(query, model.getObjectsFromTop(), model, ignore);
	}
	
	// returns first object below query in the z-order that overlaps query
	public static CanvasObject getObjectBelow(CanvasObject query,
			CanvasModel model, Collection<? extends CanvasObject> ignore) {
		return getPrevious(query, model.getObjectsFromBottom(), model, ignore);
	}
	
	private static CanvasObject getPrevious(CanvasObject query,
			List<CanvasObject> objs, CanvasModel model,
			Collection<? extends CanvasObject> ignore) {
		int index = getIndex(query, objs);
		if (index <= 0) {
			return null;
		} else {
			Set<CanvasObject> set = toSet(model.getObjectsOverlapping(query));
			ListIterator<CanvasObject> it = objs.listIterator(index);
			while (it.hasPrevious()) {
				CanvasObject o = it.previous();
				if (set.contains(o) && !ignore.contains(o)) return o;
			}
			return null;
		}
	}
	
	
	private static int getIndex(CanvasObject query, List<CanvasObject> objs) {
		int index = -1;
		for (CanvasObject o : objs) {
			index++;
			if (o == query) return index;
		}
		return -1;
	}

}
