/*******************************************************************************
 * Copyright (c) 2008 SAS Institute Inc.
 * 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:
 *     SAS Institute Inc. - initial API and implementation
 ******************************************************************************/

package org.eclipse.cosmos.me.provisional.deployment.sdd.tooling.btg.aggregator;

import java.math.BigInteger;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.ConditionalDerivedVariable;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.ConditionalDerivedVariableExpression;

public class ConditionalDerivedVariablesType implements Comparator<ConditionalDerivedVariable>
{	
	private static ConditionalDerivedVariablesType instance;

	static Collection<ConditionalDerivedVariable> merge(Collection<ConditionalDerivedVariable> coll1, Collection<ConditionalDerivedVariable> coll2)
	{
		SortedSet<ConditionalDerivedVariable> merged = new TreeSet<ConditionalDerivedVariable>();
		// put the variables in sorted sets.  Create TreeSet using the constructor
		// that takes a Comparator, using an instance of this class as the Comparator
		SortedSet<ConditionalDerivedVariable> left = new TreeSet<ConditionalDerivedVariable>(getInstance());
		SortedSet<ConditionalDerivedVariable> right = new TreeSet<ConditionalDerivedVariable>(getInstance());
		
		left.addAll(coll1);
		left.addAll(coll2);
		
		// TODO: we need to clear priorities in some situations
		//boolean clearPriorities = false;
		
		// Walk down the left list,
		for(Iterator<ConditionalDerivedVariable> i = left.iterator(); i.hasNext();) {
			ConditionalDerivedVariable varLeft = i.next();
			ConditionalDerivedVariable varRight = null;
			boolean matches = false;
			
			// checking if each item is equal to anything in the right list
			for(Iterator<ConditionalDerivedVariable> i2 = right.iterator(); i2.hasNext();) {
				varRight = i2.next();
				
				if(equals(varLeft, varRight))	{					
					matches = true;
					break;
				}
			}
			
			// If an item is equal to something in the right list,
			if(matches) {
				// replace the left item with the item from the right
				merged.add(varRight);
				
				// and everything in the right list above that item
				Set<ConditionalDerivedVariable> above = right.subSet(right.first(), varRight);
				
				merged.addAll(above);
				
				// (excluding items that have already been copied to the left list)
				above.clear();
				right.remove(varRight);
			}
			else {
				merged.add(varLeft);
			}
		}
		
		// if any un-copied items remain in the right list, append them to the end of the merged list
		if(right.size() > 0) {
			merged.addAll(right);
		}

		return merged;
	}
	
	private static boolean equals(ConditionalDerivedVariable var1, ConditionalDerivedVariable var2)
	{
		boolean equal = true;
		ConditionalDerivedVariableExpression expression1 = var1.getConditionalDerivedExpressions().iterator().next();
		ConditionalDerivedVariableExpression expression2 = var2.getConditionalDerivedExpressions().iterator().next();
		
		equal = equal && ConditionType.equalsCondition(expression1.getCondition(), expression2.getCondition());
		equal = equal && StringType.equals(expression1.getExpression(), expression2.getExpression());

		return equal;
	}

	public int compare(ConditionalDerivedVariable var1, ConditionalDerivedVariable var2)
	{
		int comparison;
		
		// the SPI code strangely returns a collection of CDV expressions, but the schema says that
		// there is one and only one, so this shortcut code is safe 
		ConditionalDerivedVariableExpression expression1 = var1.getConditionalDerivedExpressions().iterator().next();
		ConditionalDerivedVariableExpression expression2 = var2.getConditionalDerivedExpressions().iterator().next();
		
		BigInteger priority1 = expression1.getPriority();
		BigInteger priority2 = expression2.getPriority();
		
		// vars with priority are always greater than vars without
		if(priority1 != null && priority2 == null) {
			comparison = 1;
		}
		// vars without priority are always less than vars with priority
		else if(priority1 == null && priority2 != null) {
			comparison = -1;
		}
		// use BigInteger.compareTo() to compare priority values, but reverse
		// the direction of the comparison so that 1 is the highest
		else if(priority1 != null && priority2 != null) {
			comparison = priority2.compareTo(priority1);
		}		
		// both vars without priority: vars are equal
		else {
			comparison = 0;
		}
		
		return comparison;
	}
	
	private static synchronized ConditionalDerivedVariablesType getInstance()
	{
		if(instance == null) {
			instance = new ConditionalDerivedVariablesType();
		}
		
		return instance;
	}
}