/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc., Oracle Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *   Oracle Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.extender.internal.dependencies.shutdown;

import java.util.Arrays;

import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;

/**
 * Comparator based dependency sorter.
 *
 * @author Costin Leau
 * @author Andy Piper
 */
public class ComparatorServiceDependencySorter implements ServiceDependencySorter {

	public Bundle[] computeServiceDependencyGraph(Bundle[] bundles) {
		TarganStronglyConnectedSorter parser = new TarganStronglyConnectedSorter(bundles);
		return parser.computeServiceDependencyGraph();
	}

	/**
	 * Strongly Connected Component graph algorithm due to R. E. Targan, 1972. The implementation
	 * is adapted from "Algorithms in C" by Robert Sedgewick. In particular we make use of the property that
	 * non-connected components are traversed depth-first and so provide us with a regular dependency graph.
	 * Strongly connected components (a.k.a cycles) are gathered in a batch which gives us an opportunity to do
	 * futher sorting before output.
	 */
	public static class TarganStronglyConnectedSorter {
		private BundleDependencyComparator comparator = new BundleDependencyComparator();

		// Note that variable names reflect those in Sedgewick for easy comparison.
		private int id = 0;
		private int[] val; // visited list
		private int[] stack;
		private int p = 0; // current stack position
		private Node[] adj; // adjacency list of edges
		private int V; // the number of Vertices
		private Bundle[] bundles; // the output bundle list
		private Bundle[] sourcebundles; // the input bundle list
		private int index = 0; // current position in output list

		private static class Node {
			private int v;
			private Node next;

			public Node(int v, Node next) {
				this.v = v;
				this.next = next;
			}

			public int v() {
				return v;
			}

			public Node next() {
				return next;
			}
		}

		public TarganStronglyConnectedSorter(Bundle[] bundles) {
			this.V = bundles.length;
			this.bundles = new Bundle[V];
			this.sourcebundles = bundles;
			val = new int[V + 1];
			adj = new Node[V + 1];
			stack = new int[V + 1];
		}

		public Bundle[] computeServiceDependencyGraph() {
			// Zero adjacency matrix
			for (int k = 1; k <= V; k++) {
				val[k] = 0;
				adj[k] = null;
			}
			// Build adjacency matrix, x -> y
			// This probably could be more efficient
			for (int y = 1; y <= V; y++) {
				for (int x = 1; x <= V; x++) {
					if (references(sourcebundles[x - 1], sourcebundles[y - 1])) {
						adj[y] = new Node(x, adj[y]);
					}
				}
			}
			// Modified depth-first search of the nodes
			for (int k = 1; k <= V; k++) {
				if (val[k] == 0) visit(k);
			}
			return bundles;
		}

		private int visit(int k) {
			int m, min;
			val[k] = ++id;
			min = id;
			stack[p++] = k;
			for (Node t = adj[k]; t != null; t = t.next()) {
				m = (val[t.v()] == 0) ? visit(t.v()) : val[t.v()];
				if (m < min) min = m;
			}
			if (min == val[k]) {
				int subset = index;
				while (stack[p] != k) {
					int visited = stack[--p];
					bundles[index++] = sourcebundles[visited - 1];
					val[visited] = V + 1;
				}
				// Strongly connected set, so sort via ranking
				if (index > subset) {
					Arrays.sort(bundles, subset, index, comparator);
				}
			}
			return min;
		}

		/**
		 * Answer whether Bundle b is directly referenced by Bundle a
		 */
		protected static boolean references(Bundle a, Bundle b) {
			ServiceReference[] services = b.getRegisteredServices();
			if (services == null) {
				return false;
			}
			for (int i = 0; i < services.length; i++) {
				// filter on spring managed services
				if (BundleDependencyComparator.isSpringManagedService(services[i])) {
					Bundle[] referingBundles = services[i].getUsingBundles();
					if (referingBundles != null) {
						for (int j = 0; j < referingBundles.length; j++) {
							if (a.equals(referingBundles[j])) {
								return true;
							}
						}
					}
				}
			}
			return false;
		}
	}
}
