/*******************************************************************************
 * Copyright (c) 2005, 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.graph;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class NestingTree {

	List contents = new ArrayList();
	boolean isLeaf = true;
	int size;
	double sortValue;
	Node subgraph;

	private static void addToNestingTree(Map map, Node child) {
		Subgraph subgraph = child.getParent();
		NestingTree parent = (NestingTree) map.get(subgraph);
		if (parent == null) {
			parent = new NestingTree();
			parent.subgraph = subgraph;
			map.put(subgraph, parent);
			if (subgraph != null)
				addToNestingTree(map, parent);
		}
		parent.contents.add(child);
	}

	private static void addToNestingTree(Map map, NestingTree branch) {
		Subgraph subgraph = branch.subgraph.getParent();
		NestingTree parent = (NestingTree) map.get(subgraph);
		if (parent == null) {
			parent = new NestingTree();
			parent.subgraph = subgraph;
			map.put(subgraph, parent);
			if (subgraph != null)
				addToNestingTree(map, parent);
		}
		parent.contents.add(branch);
	}

	static NestingTree buildNestingTreeForRank(Rank rank) {
		Map nestingMap = new HashMap();

		for (int j = 0; j < rank.count(); j++) {
			Node node = rank.getNode(j);
			addToNestingTree(nestingMap, node);
		}

		return (NestingTree) nestingMap.get(null);
	}

	void calculateSortValues() {
		int total = 0;
		for (int i = 0; i < contents.size(); i++) {
			Object o = contents.get(i);
			if (o instanceof NestingTree) {
				isLeaf = false;
				NestingTree e = (NestingTree) o;
				e.calculateSortValues();
				total += (int) (e.sortValue * e.size);
				size += e.size;
			} else {
				Node n = (Node) o;
				n.sortValue = n.index;
				total += n.index;
				size++;
			}
		}
		sortValue = (double) total / size;
	}

	void getSortValueFromSubgraph() {
		if (subgraph != null)
			sortValue = subgraph.sortValue;
		for (int i = 0; i < contents.size(); i++) {
			Object o = contents.get(i);
			if (o instanceof NestingTree)
				((NestingTree) o).getSortValueFromSubgraph();
		}
	}

	void recursiveSort(boolean sortLeaves) {
		if (isLeaf && !sortLeaves)
			return;
		boolean change = false;
		// Use modified bubble sort for almost-sorted lists.
		do {
			change = false;
			for (int i = 0; i < contents.size() - 1; i++)
				change |= swap(i);
			if (!change)
				break;
			change = false;
			for (int i = contents.size() - 2; i >= 0; i--)
				change |= swap(i);
		} while (change);
		for (int i = 0; i < contents.size(); i++) {
			Object o = contents.get(i);
			if (o instanceof NestingTree)
				((NestingTree) o).recursiveSort(sortLeaves);
		}
	}

	void repopulateRank(Rank r) {
		for (int i = 0; i < contents.size(); i++) {
			Object o = contents.get(i);
			if (o instanceof Node)
				r.add(o);
			else
				((NestingTree) o).repopulateRank(r);
		}
	}

	boolean swap(int index) {
		Object left = contents.get(index);
		Object right = contents.get(index + 1);
		double iL = (left instanceof Node) ? ((Node) left).sortValue
				: ((NestingTree) left).sortValue;
		double iR = (right instanceof Node) ? ((Node) right).sortValue
				: ((NestingTree) right).sortValue;
		if (iL <= iR)
			return false;
		contents.set(index, right);
		contents.set(index + 1, left);
		return true;
	}

	public String toString() {
		return "Nesting:" + subgraph; //$NON-NLS-1$
	}

}