/*******************************************************************************
 * Copyright (c) 2006, 2008 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.ui.includebrowser;

import java.util.ArrayList;
import java.util.HashSet;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.swt.widgets.Display;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.cdt.internal.ui.viewsupport.AsyncTreeContentProvider;

/** 
 * This is the content provider for the include browser.
 */
public class IBContentProvider extends AsyncTreeContentProvider {

	private static final IProgressMonitor NPM = new NullProgressMonitor();
	private boolean fComputeIncludedBy = true;

	/**
	 * Constructs the content provider.
	 */
	public IBContentProvider(Display disp) {
		super(disp);
	}

	@Override
	public Object getParent(Object element) {
		if (element instanceof IBNode) {
			IBNode node = (IBNode) element;
			return node.getParent();
		}
		return super.getParent(element);
	}

	@Override
	protected Object[] syncronouslyComputeChildren(Object parentElement) {
		if (parentElement instanceof ITranslationUnit) {
			ITranslationUnit tu = (ITranslationUnit) parentElement;
			return new Object[] { new IBNode(null, new IBFile(tu), null, 0, 0, 0) };
		}
		if (parentElement instanceof IBNode) {
			IBNode node = (IBNode) parentElement;
			if (node.isRecursive() || node.getRepresentedIFL() == null) {
				return NO_CHILDREN;
			}
		}
		// allow for async computation
		return null;
	}

	@Override
	protected Object[] asyncronouslyComputeChildren(Object parentElement, IProgressMonitor monitor) {
		if (parentElement instanceof IBNode) {
			IBNode node = (IBNode) parentElement;
			IIndexFileLocation ifl= node.getRepresentedIFL();
			ICProject project= node.getCProject();
			if (ifl == null) {
				return NO_CHILDREN;
			}
			
			IIndex index;
			try {
				ICProject[] scope= CoreModel.getDefault().getCModel().getCProjects();
				index= CCorePlugin.getIndexManager().getIndex(scope);
				index.acquireReadLock();
			} catch (CoreException e) {
				CUIPlugin.log(e);
				return NO_CHILDREN;
			} catch (InterruptedException e) {
				return NO_CHILDREN;
			}
			
			try {
				IBFile directiveFile= null;
				IBFile targetFile= null;
				IIndexInclude[] includes;
				if (fComputeIncludedBy) {
					includes= findIncludedBy(index, ifl, NPM);
				}
				else {
					includes= findIncludesTo(index, ifl, NPM);
					directiveFile= node.getRepresentedFile();
				}
				if (includes.length > 0) {
					ArrayList<IBNode> result= new ArrayList<IBNode>(includes.length);
					for (int i = 0; i < includes.length; i++) {
						IIndexInclude include = includes[i];
						try {
							if (fComputeIncludedBy) {
								directiveFile= targetFile= new IBFile(project, include.getIncludedByLocation());
							}
							else {
								IIndexFileLocation includesPath= include.getIncludesLocation();
								if (includesPath == null) {
									targetFile= new IBFile(include.getName());
								}
								else {
									targetFile= new IBFile(project, includesPath);
								}
							}
							IBNode newnode= new IBNode(node, targetFile, directiveFile, 
									include.getNameOffset(), 
									include.getNameLength(), 
									include.getIncludedBy().getTimestamp());
							newnode.setIsActiveCode(include.isActive());
							newnode.setIsSystemInclude(include.isSystemInclude());
							result.add(newnode);
						}
						catch (CoreException e) {
							CUIPlugin.log(e);
						}
					}

					return result.toArray();
				}
			}
			finally {
				index.releaseReadLock();
			}
		}
		return NO_CHILDREN;
	}

	
	
	public void setComputeIncludedBy(boolean value) {
		fComputeIncludedBy = value;
	}

	public boolean getComputeIncludedBy() {
		return fComputeIncludedBy;
	}
	
	
	private IIndexInclude[] findIncludedBy(IIndex index, IIndexFileLocation ifl, IProgressMonitor pm) {
		try {
			if (ifl != null) {
				IIndexFile[] files= index.getFiles(ifl);
				if (files.length == 1) {
					return index.findIncludedBy(files[0]);
				}
				if (files.length > 0) {
					ArrayList<IIndexInclude> list= new ArrayList<IIndexInclude>();
					HashSet<IIndexFileLocation> handled= new HashSet<IIndexFileLocation>();
					for (int i = 0; i < files.length; i++) {
						final IIndexInclude[] includes = index.findIncludedBy(files[i]);
						for (int j = 0; j < includes.length; j++) {
							IIndexInclude indexInclude = includes[j];
							if (handled.add(indexInclude.getIncludedByLocation())) {
								list.add(indexInclude);
							}
						}
					}
					return list.toArray(new IIndexInclude[list.size()]);
				}
			}
		}
		catch (CoreException e) {
			CUIPlugin.log(e);
		} 
		return new IIndexInclude[0];
	}

	public IIndexInclude[] findIncludesTo(IIndex index, IIndexFileLocation ifl, IProgressMonitor pm) {
		try {
			if (ifl != null) {
				IIndexFile[] files= index.getFiles(ifl);
				if (files.length == 1) {
					return index.findIncludes(files[0]);
				}
				if (files.length > 0) {
					ArrayList<IIndexInclude> list= new ArrayList<IIndexInclude>();
					HashSet<IIndexFileLocation> handled= new HashSet<IIndexFileLocation>();
					for (int i = 0; i < files.length; i++) {
						final IIndexInclude[] includes = index.findIncludes(files[i]);
						for (int j = 0; j < includes.length; j++) {
							IIndexInclude indexInclude = includes[j];
							if (handled.add(indexInclude.getIncludesLocation())) {
								list.add(indexInclude);
							}
						}
					}
					return list.toArray(new IIndexInclude[list.size()]);
				}
			}
		}
		catch (CoreException e) {
			CUIPlugin.log(e);
		} 
		return new IIndexInclude[0];
	}
}
