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

/**
 * A group of nodes which are interlocked and cannot be separately placed.
 * @since 3.1
 */
class NodeCluster extends NodeList {
	
int hashCode = new Object().hashCode();

boolean isSetMember;
boolean isDirty;
boolean leftDirty;
boolean rightDirty;

int leftFreedom;
int rightFreedom;
int leftNonzero;
int rightNonzero;
int leftCount = 0;
int rightCount = 0;

CollapsedEdges leftLinks[] = new CollapsedEdges[10];
CollapsedEdges rightLinks[] = new CollapsedEdges[10];
NodeCluster leftNeighbors[] = new NodeCluster[10];
NodeCluster rightNeighbors[] = new NodeCluster[10];

int effectivePull;
int weightedTotal;
int weightedDivisor;
int unweightedTotal;
int unweightedDivisor;

void addLeftNeighbor(NodeCluster neighbor, CollapsedEdges link) {
	//Need to grow array in the following case
	if (leftNeighbors.length == leftCount) {
		int newSize = leftNeighbors.length * 2;
		
		NodeCluster newNeighbors[] = new NodeCluster[newSize];
		CollapsedEdges newLinks[] = new CollapsedEdges[newSize];

		System.arraycopy(leftNeighbors, 0, newNeighbors, 0, leftNeighbors.length);
		System.arraycopy(leftLinks, 0, newLinks, 0, leftLinks.length);

		leftNeighbors = newNeighbors;
		leftLinks = newLinks;
	}
	leftNeighbors[leftCount] = neighbor;
	leftLinks[leftCount++] = link;
}

void addRightNeighbor(NodeCluster neighbor, CollapsedEdges link) {
	if (rightNeighbors.length == rightCount) {
		int newSize = rightNeighbors.length * 2;
		
		NodeCluster newNeighbors[] = new NodeCluster[newSize];
		CollapsedEdges newLinks[] = new CollapsedEdges[newSize];

		System.arraycopy(rightNeighbors, 0, newNeighbors, 0, rightNeighbors.length);
		System.arraycopy(rightLinks, 0, newLinks, 0, rightLinks.length);

		rightNeighbors = newNeighbors;
		rightLinks = newLinks;
	}
	rightNeighbors[rightCount] = neighbor;
	rightLinks[rightCount++] = link;
}

public void adjustRank(int delta, Collection affected) {
	adjustRank(delta);
	NodeCluster neighbor;
	CollapsedEdges edges;
	for (int i = 0; i < leftCount; i++) {
		neighbor = leftNeighbors[i];
		if (neighbor.isSetMember)
			continue;
		edges = leftLinks[i];
		
		neighbor.weightedTotal += delta * edges.collapsedWeight;
		neighbor.unweightedTotal += delta * edges.collapsedCount;

		weightedTotal -= delta * edges.collapsedWeight;
		unweightedTotal -= delta * edges.collapsedCount;
		
		neighbor.rightDirty = leftDirty = true;
		if (!neighbor.isDirty) {
			neighbor.isDirty = true;
			affected.add(neighbor);
		}
	}
	for (int i = 0; i < rightCount; i++) {
		neighbor = rightNeighbors[i];
		if (neighbor.isSetMember)
			continue;
		edges = rightLinks[i];

		neighbor.weightedTotal += delta * edges.collapsedWeight;
		neighbor.unweightedTotal += delta * edges.collapsedCount;
		
		weightedTotal -= delta * edges.collapsedWeight;
		unweightedTotal -= delta * edges.collapsedCount;
		
		neighbor.leftDirty = rightDirty = true;
		if (!neighbor.isDirty) {
			neighbor.isDirty = true;
			affected.add(neighbor);
		}
	}
	isDirty = true;
	affected.add(this);
}

public boolean equals(Object o) {
	return o == this;
}

CollapsedEdges getLeftNeighbor(NodeCluster neighbor) {
	for (int i = 0; i < leftCount; i++) {
		if (leftNeighbors[i] == neighbor)
			return leftLinks[i];
	}
	return null;
}

int getPull() {
	return effectivePull;
}

CollapsedEdges getRightNeighbor(NodeCluster neighbor) {
	for (int i = 0; i < rightCount; i++) {
		if (rightNeighbors[i] == neighbor)
			return rightLinks[i];
	}
	return null;
}

public int hashCode() {
	return hashCode;
}

/**
 * Initializes pull and freedom values.
 */
void initValues() {
	weightedTotal = 0;
	weightedDivisor = 0;
	unweightedTotal = 0;
	int slack;
	
	leftNonzero = rightNonzero = leftFreedom = rightFreedom = Integer.MAX_VALUE;
	for (int i = 0; i < leftCount; i++) {
		CollapsedEdges edges = leftLinks[i];
		weightedTotal -= edges.getWeightedPull();
		unweightedTotal -= edges.tightestEdge.getSlack();
		unweightedDivisor += edges.collapsedCount;
		weightedDivisor += edges.collapsedWeight;
		slack = edges.tightestEdge.getSlack();
		leftFreedom = Math.min(slack, leftFreedom);
		if (slack > 0)
			leftNonzero = Math.min(slack, leftNonzero);
	}
	for (int i = 0; i < rightCount; i++) {
		CollapsedEdges edges = rightLinks[i];
		weightedTotal += edges.getWeightedPull();
		unweightedDivisor += edges.collapsedCount;
		unweightedTotal += edges.tightestEdge.getSlack();
		weightedDivisor += edges.collapsedWeight;
		slack = edges.tightestEdge.getSlack();
		rightFreedom = Math.min(slack, rightFreedom);
		if (slack > 0)
			rightNonzero = Math.min(slack, rightNonzero);
	}
	updateEffectivePull();
}

/**
 * Refreshes the left and right freedom.
 */
void refreshValues() {
	int slack;
	isDirty = false;
	if (leftDirty) {
		leftDirty = false;
		leftNonzero = leftFreedom = Integer.MAX_VALUE;
		for (int i = 0; i < leftCount; i++) {
			CollapsedEdges edges = leftLinks[i];
			slack = edges.tightestEdge.getSlack();
			leftFreedom = Math.min(slack, leftFreedom);
			if (slack > 0)
				leftNonzero = Math.min(slack, leftNonzero);
		}
	}
	if (rightDirty) {
		rightDirty = false;
		rightNonzero = rightFreedom = Integer.MAX_VALUE;
		for (int i = 0; i < rightCount; i++) {
			CollapsedEdges edges = rightLinks[i];
			slack = edges.tightestEdge.getSlack();
			rightFreedom = Math.min(slack, rightFreedom);
			if (slack > 0)
				rightNonzero = Math.min(slack, rightNonzero);
		}
	}
	updateEffectivePull();
}

private void updateEffectivePull() {
	if (weightedDivisor != 0)
		effectivePull = weightedTotal / weightedDivisor;
	else if (unweightedDivisor != 0)
			effectivePull = unweightedTotal / unweightedDivisor;
	else
		effectivePull = 0;
}

}