/*******************************************************************************
 * 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.ArrayList;
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.Identity;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.Resource;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.artifact.Artifact;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.contentunit.Completion;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.contentunit.CompositeLocalizationUnit;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.contentunit.InstallableUnit;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.contentunit.ResultingChange;
import org.eclipse.cosmos.me.provisional.deployment.sdd.common.spi.contentunit.ResultingResource;

public class InstallableUnitType {
	public static InstallableUnit merge(InstallableUnit installableUnit1, InstallableUnit installableUnit2)
	{	
		//may not have ResultingResource or ResultingChange
		
		// compare ResultingResource
		//TODO ASSUMES ONLY ONE
		
		ResultingResource resource1 = (ResultingResource) installableUnit1.getResultingResources().iterator().next();
		ResultingResource resource2 = (ResultingResource) installableUnit2.getResultingResources().iterator().next();
		
		/*if (ResultingResourceType.equalsResultingResource(resource1, resource2))
		{
			
		}
		*/
		
		
		//compare TargetResources  No targetresourceref???
		InstallableUnit iU1 = (InstallableUnit) installableUnit1;				

		// ***** InstallationArtifactType Rule *****
		//check the 4 artifact types, if resources, targetref, and resulting resource match but artifacts don't,
		//log error and pick artifacts from iu1
		
		// InstallArtifact merge or log error
		Artifact art1 = installableUnit1.getInstallArtifact();
		Artifact art2 = installableUnit2.getInstallArtifact();
		
		if(!mergeInstallationArtifact(art1,art2,installableUnit1,"",1)){
			//TODO: Log error
		}
		
		// UndoArtifact merge or log error
		art1 = installableUnit1.getUndoArtifact();
		art2 = installableUnit2.getUndoArtifact();
		
		if(!mergeInstallationArtifact(art1,art2,installableUnit1,"",2)){
			//TODO: Log error
		}
		
		// UninstallArtifact merge or log error
		art1 = installableUnit1.getUninstallArtifact();
		art2 = installableUnit2.getUninstallArtifact();
		
		if(!mergeInstallationArtifact(art1,art2,installableUnit1,"",3)){
			//TODO: Log error
		}
		
		// UpdateArtifact merge or log error
		art1 = installableUnit1.getUpdateArtifact();
		art2 = installableUnit2.getUpdateArtifact();
		
		if(!mergeInstallationArtifact(art1,art2,installableUnit1,"",4)){
			//TODO: Log error
		}
		
		// merge completions
		Set<Completion> mergedCompletions = new HashSet<Completion>();
		
		CompletionType.merge(installableUnit1.getInstallableUnitCompletion(), mergedCompletions);
		CompletionType.merge(installableUnit2.getInstallableUnitCompletion(), mergedCompletions);
		
		installableUnit1.setCompletion(mergedCompletions);
		
		// ***** End InstallationArtifact Rule *****
		
		return installableUnit1;
		
	}
	
	private static boolean mergeInstallationArtifact( Artifact art1, Artifact art2, InstallableUnit installableUnit1, String logMsg, int type){
	//helper method to merge each InstallationArtifact type
		
		boolean merged = false;
		
		if( art1 == null && art2 == null){
			//both null, nothing to do
			merged = true;
		}
		else if(ArtifactType.equals(art1, art2)){
			Artifact retArt = ArtifactType.merge(art1, art2);
			if( retArt != null){
				switch (type){
				case 1:
					installableUnit1.setInstallArtifact(retArt);
					merged = true;
				case 2:
					installableUnit1.setUndoArtifact(retArt);
					merged = true;
				case 3:
					installableUnit1.setUninstallArtifact(retArt);
					merged = true;
				case 4:
					installableUnit1.setUpdateArtifact(retArt);
					merged = true;
				default:
					//TODO: Log error
				}
					
			}
		}
		else{
			//TODO: Log error
		}
		
		return merged;
	}
	
	private static boolean equals (InstallableUnit unit1, InstallableUnit unit2)
	{
		//may not have ResultingResource or ResultingChange
		//definitely need to check target though
		
		if ((unit1.getResultingResources()==null)&&(unit2.getResultingResources()!=null))
			return false;
		if ((unit1.getResultingResources()!=null)&&(unit2.getResultingResources()==null))
			return false;
		//TODO assumes only 1
		else
		{
			ResultingResource r1 = unit1.getResultingResources().iterator().next();
			ResultingResource r2 = unit2.getResultingResources().iterator().next();
			if (!ResultingResourceType.equals(r1, r2))
				return false;
		}
		
		if ((unit1.getResultingChanges()==null)&&(unit2.getResultingChanges()!=null))
			return false;
		if ((unit1.getResultingChanges()!=null)&&(unit2.getResultingChanges()==null))
			return false;
		//TODO assumes only 1
		else
		{
			ResultingChange r1 = unit1.getResultingChanges().iterator().next();
			ResultingChange r2 = unit2.getResultingChanges().iterator().next();
			if (!ResultingChangeType.equals(r1, r2))
				return false;
		}
		
		if (!ResourceType.equals(Util.resolveResourceRef(unit1.getTargetResourceReference()),Util.resolveResourceRef(unit2.getTargetResourceReference())))
			return false;
		
		return true;
	}
	
	public static Collection <InstallableUnit> merge(Collection <InstallableUnit> installableUnits1, Collection <InstallableUnit> installableUnits2)	
	{
		
		Collection <InstallableUnit> installableUnitsToRemove = new ArrayList <InstallableUnit> ();
		Iterator <InstallableUnit> installableUnitIter1 = installableUnits1.iterator();		
		//iterate through the installableUnits
		while (installableUnitIter1.hasNext())
		{
			InstallableUnit installableUnit1 = (InstallableUnit) installableUnitIter1.next();
			Iterator <InstallableUnit> installableUnitIter2 = installableUnits2.iterator();
			while (installableUnitIter2.hasNext())
			{
				InstallableUnit installableUnit2 = (InstallableUnit) installableUnitIter2.next();
				//if you find a match that hasn't been found, note it
				//one of the equals needs to be deleted with its children merged over
				if (installableUnitsToRemove.contains(installableUnit2)==false)
				{
					if (equals(installableUnit1, installableUnit2))
					{  //merge needed mergings
						installableUnitsToRemove.add(installableUnit2);												
						installableUnit1 = merge(installableUnit1,installableUnit2);						
					}
				}
					
			}
		}
		
		installableUnits2.removeAll(installableUnitsToRemove);
		//remove all of the matches that were found
		
		Collection <InstallableUnit> finalInstallableUnits = new ArrayList <InstallableUnit> ();
		//create and return a new collection with all of the remaining items
		finalInstallableUnits.addAll(installableUnits1);
		finalInstallableUnits.addAll(installableUnits2);		
		
		return finalInstallableUnits;		
		
	}
	
}
