/*******************************************************************************
 * Copyright (c) 2007 Intel 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:
 * Intel Corporation - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IModificationStatus;
import org.eclipse.cdt.managedbuilder.core.IResourceInfo;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;

public class ToolListModificationInfo {
	private ToolInfo[] fResultingTools;
	private ToolInfo[] fAddedTools;
	private ToolInfo[] fRemovedTools;
	private IResourceInfo fRcInfo;
	
	ToolListModificationInfo(IResourceInfo rcInfo, ToolInfo[] resultingTools, ToolInfo[] added, ToolInfo[] removed, ToolInfo[] remaining){
		fResultingTools = resultingTools;
		fRemovedTools = removed;
		fAddedTools = added;
		fRcInfo = rcInfo;
	}
	
	public IResourceInfo getResourceInfo(){
		return fRcInfo;
	}
	
	public List<ITool> getResultingToolList(List<ITool> list) {
		if(list == null)
			list = new ArrayList<ITool>(fResultingTools.length);
		
		for(int i = 0; i < fResultingTools.length; i++){
			list.add(fResultingTools[i].getResultingTool());
		}
		
		return list;
	}
	
	public ITool[] getResultingTools() {
		ITool[] tools = new ITool[fResultingTools.length];
		
		
		for(int i = 0; i < fResultingTools.length; i++){
			tools[i] = fResultingTools[i].getResultingTool();
		}
		
		return tools;
	}
	
	public ITool[] getRemovedTools() {
		return toToolArray(fRemovedTools, true);
	}

	public ITool[] getAddedTools(boolean resulting) {
		return toToolArray(fAddedTools, !resulting);
	}

	public ITool[] getRemainedTools() {
		return toToolArray(fAddedTools, true);
	}
	
	private static ITool[] toToolArray(ToolInfo[] infos, boolean initialTools){
		ITool[] tools = new ITool[infos.length];
		
		for(int i = 0; i < infos.length; i++){
			tools[i] = initialTools ? infos[i].getInitialTool() : infos[i].getResultingTool();
		}
		
		return tools;
	}
	
	private static ITool[][] toToolArray(ToolInfo[][] infos, boolean initialTools){
		ITool[][] tools = new ITool[infos.length][];
		
		for(int i = 0; i < infos.length; i++){
			tools[i] = toToolArray(infos[i], initialTools);
		}
		
		return tools;
	}
	


	public MultiStatus getModificationStatus(){
		List<IModificationStatus> statusList = new ArrayList<IModificationStatus>();

		ToolInfo[][] conflictInfos = calculateConflictingTools(fResultingTools);
		ITool[][] conflicting = toToolArray(conflictInfos, true);
		
		Map<String, String> unspecifiedRequiredProps = new HashMap<String, String>();
		Map<String, String> unspecifiedProps = new HashMap<String, String>();
		Set<String> undefinedSet = new HashSet<String>();
		IConfiguration cfg = fRcInfo.getParent();
		ITool[] nonManagedTools = null;
		if(cfg.isManagedBuildOn() && cfg.supportsBuild(true)){
			List<ITool> list = new ArrayList<ITool>();
			for(int i = 0; i < fResultingTools.length; i++){
				if(!fResultingTools[i].getInitialTool().supportsBuild(true)){
					list.add(fResultingTools[i].getInitialTool());
				}
			}
			if(list.size() != 0){
				nonManagedTools = list.toArray(new Tool[list.size()]);
			}
		}
		
		IModificationStatus status = new ModificationStatus(unspecifiedRequiredProps, unspecifiedProps, undefinedSet, conflicting, nonManagedTools);

		if(status.getSeverity() != IStatus.OK)
			statusList.add(status);
		
		for(int i = 0; i < fResultingTools.length; i++){
			status = fResultingTools[i].getModificationStatus();
			if(status.getSeverity() != IStatus.OK)
				statusList.add(status);
		}
		
		if(statusList.size() != 0)
			return new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.INFO, "", null); //$NON-NLS-1$
		return new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.ERROR, "", null); //$NON-NLS-1$
	}

	private ToolInfo[][] calculateConflictingTools(ToolInfo[] infos){
		infos = filterInfos(infos);
		
		return doCalculateConflictingTools(infos);
	}
	
	private ToolInfo[] filterInfos(ToolInfo[] infos){
		if(fRcInfo instanceof FolderInfo){
			Map<ITool, ToolInfo> map = createInitialToolToToolInfoMap(infos);
			ITool[] tools = new ArrayList<ITool>(map.keySet()).toArray(new ITool[map.size()]);
		
			tools = ((FolderInfo)fRcInfo).filterTools(tools, fRcInfo.getParent().getManagedProject());
			
			if(tools.length < infos.length){
				infos = new ToolInfo[tools.length]; 
				for(int i = 0; i < infos.length; i++){
					infos[i] = map.get(tools[i]);
				}
			}
		}
		
		return infos;
	}
	
	private static Map<ITool, ToolInfo> createInitialToolToToolInfoMap(ToolInfo[] infos){
		Map<ITool, ToolInfo> map = new LinkedHashMap<ITool, ToolInfo>();
		for(int i = 0; i < infos.length; i++){
			map.put(infos[i].getInitialTool(), infos[i]);
		}
		
		return map;
	}


	private ToolInfo[][] doCalculateConflictingTools(ToolInfo[] infos){
		HashSet<ToolInfo> set = new HashSet<ToolInfo>();
		set.addAll(Arrays.asList(infos));
		List<ToolInfo[]> result = new ArrayList<ToolInfo[]>();
		for(Iterator<ToolInfo> iter = set.iterator(); iter.hasNext();){
			ToolInfo ti = iter.next();
			ITool t = ti.getInitialTool();
			iter.remove();
			@SuppressWarnings("unchecked")
			HashSet<ToolInfo> tmp = (HashSet<ToolInfo>)set.clone();
			List<ITool> list = new ArrayList<ITool>();
			for(Iterator<ToolInfo> tmpIt = tmp.iterator(); tmpIt.hasNext();){
				ToolInfo otherTi = tmpIt.next(); 
				ITool other = otherTi.getInitialTool();
				String conflicts[] = getConflictingInputExts(t, other);
				if(conflicts.length != 0){
					list.add(other);
					tmpIt.remove();
				}
			}
			
			if(list.size() != 0){
				list.add(t);
				ToolInfo[] arr = list.toArray(new ToolInfo[list.size()]);
				result.add(arr);
			}
			set = tmp;
			iter = set.iterator();
		}
		
		return result.toArray(new ToolInfo[result.size()][]);
	}
	
	private String[] getConflictingInputExts(ITool tool1, ITool tool2){
		IProject project = fRcInfo.getParent().getOwner().getProject();
		String ext1[] = ((Tool)tool1).getAllInputExtensions(project);
		String ext2[] = ((Tool)tool2).getAllInputExtensions(project);
		Set<String> set1 = new HashSet<String>(Arrays.asList(ext1));
		Set<String> result = new HashSet<String>();
		for(int i = 0; i < ext2.length; i++){
			if(set1.remove(ext2[i]))
				result.add(ext2[i]);
		}
		return result.toArray(new String[result.size()]);
	}

	public void apply(){
		((ResourceInfo)fRcInfo).doApply(this);
	}
}
