/*******************************************************************************
 * 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.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.BooleanParameter;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.IntegerParameter;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.Parameter;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.ParameterGroup;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.StringParameter;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.variable.URIParameter;

public class ParameterGroupType
{	
	static ParameterGroup merge(Collection<ParameterGroup> collection1, Collection<ParameterGroup> collection2)
	{
		// merge all the input parameter groups together to be processed
		Set<ParameterGroup> mergedSet = new HashSet<ParameterGroup>();
		
		mergedSet.addAll(collection1);
		mergedSet.addAll(collection2);
		
		// 
		ParameterGroup merged = null;
		
		// create sets for each type of parameter to hold merged parameters of that type
		Set<BooleanParameter> boolParms = new HashSet<BooleanParameter>();
		Set<IntegerParameter> intParms = new HashSet<IntegerParameter>();
		Set<StringParameter> strParms = new HashSet<StringParameter>();
		Set<URIParameter> uriParms = new HashSet<URIParameter>();

		// iterate over each of the parameter groups in the input sets, merging
		// each type as we go
		for(Iterator<ParameterGroup> groupIt = mergedSet.iterator(); groupIt.hasNext();) {
			ParameterGroup group = groupIt.next();
			
			// there's no good way to 'new' a ParameterGroup, so grab one and clone it,
			// and later replace all of its parameters
			if(merged == null) {
				merged = (ParameterGroup) group.clone();
			}
			
			merge(group.getBooleanParameters(), boolParms);
			merge(group.getIntegerParameters(), intParms);
			merge(group.getStringParameters(), strParms);
			merge(group.getURIParameters(), uriParms);
		}
		
		// if there was anything to merge, replace all of the cloned parameters
		// with the merged ones from all groups
		if(merged != null) {
			merged.setBooleanParameters(boolParms);
			merged.setIntegerParameters(intParms);
			merged.setStringParameters(strParms);
			merged.setURIParameters(uriParms);
		}
						
		return merged;
	}
	
	private static <T extends Parameter> void merge(Collection<T> newParms, Set<T> mergedParms)
	{
		for(Iterator<T> i = newParms.iterator(); i.hasNext();) {
			boolean matches = false;
			T newParm = i.next();
			
			for(Iterator<T> i2 = mergedParms.iterator(); i2.hasNext();) {
				T mergedParm = i2.next();
				
				if(equals(newParm, mergedParm)) {
					matches = true;
					
					if(newParm instanceof StringParameter) {
						StringParameterType.merge((StringParameter)newParm, (StringParameter)mergedParm);
					}
					else {
						merge(newParm, mergedParm);
					}
					break;
				}					
			}
			
			if(!matches) {
				mergedParms.add(newParm);
			}
		}
	}
	
	private static boolean equals(Parameter p1, Parameter p2)
	{
		boolean equals = true;
		
		// handle the BaseParameter values
		equals = equals && StringType.equals(p1.getDefaultValue(), p2.getDefaultValue());
		equals = equals && BooleanType.equals(p1.isSensitive(), p2.isSensitive());
		equals = equals && BooleanType.equals(p1.isRequired(), p2.isRequired());
		equals = equals && OperationsType.equals(p1.getOperations(), p2.getOperations());
		
		if(equals && p1 instanceof IntegerParameter) {
			equals = IntegerParameterType.equals((IntegerParameter)p1, (IntegerParameter)p2);
		}
				
		return equals;
	}
	
	static Parameter merge(Parameter newParm, Parameter merged)
	{
		merged.setDisplayName(TranslatableStringType.merge(newParm.getDisplayName(), merged.getDisplayName()));
		merged.setDescription(TranslatableStringType.merge(newParm.getDescription(), merged.getDescription()));
		merged.setShortDescription(TranslatableStringType.merge(newParm.getShortDescription(), merged.getShortDescription()));
		
		return merged;
	}
}