/*******************************************************************************
 * 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.core;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.cdt.core.CCProjectNature;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.settings.model.util.ResourceChangeHandlerBase;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages;
import org.eclipse.cdt.managedbuilder.internal.dataprovider.ConfigurationDataProvider;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;

class ResourceChangeHandler2 extends ResourceChangeHandlerBase{
	private class RcMoveHandler implements IResourceMoveHandler {

		public void done() {
		}

		public void handleProjectClose(IProject project) {
			sendClose(project);
			try {
				ManagedBuildManager.setLoaddedBuildInfo(project, null);
			} catch (CoreException e) {
			}
		}

		public boolean handleResourceMove(IResource fromRc, IResource toRc) {
			switch(fromRc.getType()){
			case IResource.PROJECT:
				IProject fromProject = fromRc.getProject();
				IProject toProject = toRc.getProject();
				ManagedBuildInfo info = (ManagedBuildInfo)ManagedBuildManager.getBuildInfo(fromProject, false);
				if(info != null){
					info.updateOwner(toRc);
					ManagedBuildManager.updateLoaddedInfo(fromProject, toProject, info);
				}
			}
			return false;
		}

		public boolean handleResourceRemove(IResource rc) {
			switch(rc.getType()){
			case IResource.PROJECT:
				IProject project = rc.getProject();
				sendClose(project);
				try {
					ManagedBuildManager.setLoaddedBuildInfo(project, null);
				} catch (CoreException e) {
				}
			}
			return false;
		}
	}
	
	protected IResourceMoveHandler createResourceMoveHandler(
			IResourceChangeEvent event) {
		return new RcMoveHandler();
	}
	
	public void sendClose(IProject project){
		sendClose(ManagedBuildManager.getBuildInfo(project,false));
	}

	private void sendClose(IManagedBuildInfo info){
		if(info != null){
			IManagedProject managedProj = info.getManagedProject();
			if (managedProj != null) {
				IConfiguration cfgs[] = managedProj.getConfigurations();
			
				for(int i = 0; i < cfgs.length; i++)
					ManagedBuildManager.performValueHandlerEvent(cfgs[i], IManagedOptionValueHandler.EVENT_CLOSE, true);
			}
		}
	}

	private static class Visitor implements IResourceDeltaVisitor {
		private Set fProjSet;
		
		Visitor(Set projSet){
			fProjSet = projSet;
		}

		public boolean visit(IResourceDelta delta) throws CoreException {
			IResource rc = delta.getResource();
			switch (rc.getType()) {
			case IResource.ROOT:
				return true;
			case IResource.PROJECT:
				int flags = delta.getFlags();
				if((flags & IResourceDelta.DESCRIPTION) == IResourceDelta.DESCRIPTION){
					IProject project = rc.getProject();
					IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(project);
					if(info != null && info.isValid() && info.getManagedProject() != null){
						IProjectDescription eDes = project.getDescription();
						IConfiguration[] cfgs = info.getManagedProject().getConfigurations();
						String natureIds[] = eDes.getNatureIds();
						for(int i = 0; i < cfgs.length; i++){
							String cachedIds[] = ConfigurationDataProvider.getNaturesIdsUsedOnCache(cfgs[i]);
							if(checkNaturesNeedUpdate(cachedIds, natureIds)){
								if(fProjSet == null)
									fProjSet = new HashSet();
								
								fProjSet.add(project);
								break;
							}
						}
					}
				}
				return false;
			default:
				return false;
			}
		}
		
		Set getProjSet(){
			return fProjSet;
		}
		
	}
	
	private static boolean checkNaturesNeedUpdate(String[] oldIds, String[] newIds){
		if(oldIds == null)
			return true;
		
		Set oldSet = new HashSet(Arrays.asList(oldIds));
		Set oldSetCopy = new HashSet(oldSet);
		Set newSet = new HashSet(Arrays.asList(newIds));
		oldSet.removeAll(newSet);
		newSet.removeAll(oldSetCopy);
		if(oldSet.contains(CProjectNature.C_NATURE_ID)
				|| oldSet.contains(CCProjectNature.CC_NATURE_ID)
				|| newSet.contains(CProjectNature.C_NATURE_ID)
				|| newSet.contains(CCProjectNature.CC_NATURE_ID))
			return true;
		
		return false;
	}
	
	public void resourceChanged(IResourceChangeEvent event) {
		super.resourceChanged(event);
		
		switch(event.getType()){
		case IResourceChangeEvent.POST_CHANGE:
			IResourceDelta delta = event.getDelta();
			if(delta != null){
				Visitor visitor = new Visitor(null);
				try {
					delta.accept(visitor);
				} catch (CoreException e) {
					ManagedBuilderCorePlugin.log(e);
				}
				postProcess(visitor.getProjSet());
			}
		}
	}

	private void postProcess(final Set projSet){
		if(projSet == null || projSet.size() == 0)
			return;

		IWorkspace wsp = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = wsp.getRoot();
		
		Job job = new Job(ManagedMakeMessages.getString("ResourceChangeHandler2.0")){ //$NON-NLS-1$

			protected IStatus run(IProgressMonitor monitor) {
				for(Iterator iter = projSet.iterator(); iter.hasNext();){
					IProject project = (IProject)iter.next();
					try {
						ManagedBuildManager.updateCoreSettings(project);
					} catch (CoreException e) {
						ManagedBuilderCorePlugin.log(e);
					}
				}
				return Status.OK_STATUS;
			}
			
		};
		
		job.setRule(root);
		job.setSystem(true);
		job.schedule();
	}

}
