/*******************************************************************************
 * Copyright (c) 2007, 2011 Wind River Systems, Inc. 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:
 *    Markus Schorn - initial API and implementation
 *******************************************************************************/ 
package org.eclipse.cdt.internal.core.pdom.indexer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;

public class DeltaAnalyzer {
	private final List<ITranslationUnit> fForce= new ArrayList<ITranslationUnit>();
	private final List<ITranslationUnit> fChanged= new ArrayList<ITranslationUnit>();
	private final List<ITranslationUnit> fRemoved= new ArrayList<ITranslationUnit>();
	// For testing purposes, only.
	public static boolean sSuppressPotentialTUs= false;
	
	public DeltaAnalyzer() {
	}
	
	public void analyzeDelta(ICElementDelta delta) throws CoreException {
		processDelta(delta, new HashSet<IResource>());
	}
	
	private void processDelta(ICElementDelta delta, Set<IResource> handled) throws CoreException {
		final int flags = delta.getFlags();

		final boolean hasChildren = (flags & ICElementDelta.F_CHILDREN) != 0;
		if (hasChildren) {
			for (ICElementDelta child : delta.getAffectedChildren()) {
				processDelta(child, handled);
			}
		}

		final ICElement element = delta.getElement();
		switch (element.getElementType()) {
		case ICElement.C_UNIT:
			ITranslationUnit tu = (ITranslationUnit)element;
			if (!tu.isWorkingCopy()) {
				handled.add(element.getResource());
				switch (delta.getKind()) {
				case ICElementDelta.CHANGED:
					if ((flags & ICElementDelta.F_CONTENT) != 0) {
						fChanged.add(tu);
					}
					break;
				case ICElementDelta.ADDED:
					fChanged.add(tu);
					break;
				case ICElementDelta.REMOVED:
					fRemoved.add(tu);
					break;
				}
			}
			break;
		case ICElement.C_CCONTAINER:
			ICContainer folder= (ICContainer) element;
			if (delta.getKind() == ICElementDelta.ADDED) {
				handled.add(element.getResource());
				collectSources(folder, fChanged);
			}
			break;
		}
		
		if (!sSuppressPotentialTUs) {
			// Always look at the children of the resource delta (bug 343538)
			final IResourceDelta[] rDeltas= delta.getResourceDeltas();
			processResourceDelta(rDeltas, element, handled);
		}
	}

	public void processResourceDelta(final IResourceDelta[] rDeltas, final ICElement element,
			Set<IResource> handled) {
		if (rDeltas != null) {
			for (IResourceDelta rd: rDeltas) {
				final int rdkind = rd.getKind();
				if (rdkind != IResourceDelta.ADDED) {
					IResource res= rd.getResource();
					if (!handled.add(res)) {
						continue;
					}
					if (res instanceof IFile) {
						switch (rdkind) {
						case IResourceDelta.CHANGED:
							if ((rd.getFlags() & IResourceDelta.CONTENT) != 0) {
								fChanged.add(new PotentialTranslationUnit(element, (IFile) res));
							}
							break;
						case IResourceDelta.REMOVED:
							fRemoved.add(new PotentialTranslationUnit(element, (IFile) res));
							break;
						}
					}
				}
				processResourceDelta(rd.getAffectedChildren(IResourceDelta.CHANGED | IResourceDelta.REMOVED), element, handled);
			}
		}
	}

	private void collectSources(ICContainer container, Collection<ITranslationUnit> sources) throws CoreException {
		container.accept(new TranslationUnitCollector(sources, sources, new NullProgressMonitor()));
	}

	public ITranslationUnit[] getForcedTUs() {
		return fForce.toArray(new ITranslationUnit[fForce.size()]);
	}

	public ITranslationUnit[] getChangedTUs() {
		return fChanged.toArray(new ITranslationUnit[fChanged.size()]);
	}

	public ITranslationUnit[] getRemovedTUs() {
		return fRemoved.toArray(new ITranslationUnit[fRemoved.size()]);
	}

	public List<ITranslationUnit> getForcedList() {
		return fForce;
	}

	public List<ITranslationUnit> getChangedList() {
		return fChanged;
	}
}
