/*******************************************************************************
 * Copyright (c) 2005 IBM 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:
 *     Oracle Corporation
 *******************************************************************************/
package org.eclipse.bpel.ui.details.providers;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.bpel.ui.BPELUIPlugin;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;


/**
 * Content provider for EMF objects that can be read from the Workspace 
 * 
 * @author Michal Chmielewski (michal.chmielewski@oracle.com)
 */

public abstract class AbstractResourceContentProvider extends AbstractContentProvider  {	
	
	static private final String SLASH = "/"; //$NON-NLS-1$
	
	/**
	 * Append the schemas that are present in the object passed to the list
	 * indicated.   
	 * 
	 * @param input an object that has or is schema definitions.
	 * @param list the list where the schemas are put.
	 */
	
	
	ResourceSet fResourceSet;
	 
	public AbstractResourceContentProvider ( ResourceSet set ) {

		fResourceSet = set;
	}
	
	
	protected void doCollectElements ( Object input, List list) throws CoreException {
		
		if (input == null) {
			return ;
		}
		
		if (input instanceof IContainer) {			
			findCandidates((IContainer) input,list, getKindClass() );
			return;
		}
		
 
		if (isAcceptableKind(input)) {
			list.add(input);			
			return;
		}

		Object[] arr = null;
		
		if (input.getClass().isArray()) {			
			arr = (Object[]) input;
		} else if (input instanceof List) {
			arr = ((List)input).toArray();
		}
		
		if (arr == null) {
			return;
		}
		
		for(int i=0; i < arr.length; i++) {
			doCollectElements ( arr[i], list );
		}					
	}
		
	

	@Override
	public void collectElements (Object input, List list) {
		try {
			doCollectElements ( input, list );
		} catch (CoreException e) {
			BPELUIPlugin.log(e);
		}	
	}				
	
	
	
	/**
	 * Check of the object passed is of the acceptable kind. This simply
	 * checks to see if the the result returned by {@link #getKindClass()}
	 * matches the object passed.
	 *  
	 * @param obj the object to test.
	 * @return true/false, depending on outcomee.
	 */
	
	protected boolean isAcceptableKind ( Object obj ) {
		
		Class [] kind = getKindClass();
		
		if (kind == null) {
			return true;
		}
		
		for(int i=0; i < kind.length; i++) {
			if (kind[i].isInstance(obj)) {
				return true;
			}
		}
		
		return false;
	}
	
	
	
	/**
	 * Find the candidate objects to load.
	 * 
	 * @param container
	 * @param list
	 * @param the kind of EMF objects that we want back
	 * @return
	 * @throws CoreException
	 */
	
	protected List findCandidates ( IContainer container, List list, Class [] kind ) throws CoreException {
		
		if (list == null) {
			list = new LinkedList();
		}		
		
		Iterator i = findCandidates( container ).iterator();
		while (i.hasNext()) {
			IResource r = (IResource) i.next();
			try {
				Object obj = load(r);
				if ( isAcceptableKind (obj) ) {
					list.add(obj);
				} 
			} catch (Exception ex) {
				BPELUIPlugin.log(ex);
			}
		}
		return list;
	}	

	
	/**
	 * Load the EMF model from the resource indicated. Return the top EMF object for the model. 
	 *  
	 * @param r
	 * @return
	 */
	
	protected Object load ( IResource r ) 
    {
    	// Format: /Project/path    	
    	String uri = SLASH + r.getProject().getName() + SLASH + r.getProjectRelativePath();
        URI locationURI = URI.createPlatformResourceURI( uri );       
            
        Resource resource = fResourceSet.getResource(locationURI, true);        
        return resource.getContents().get(0);               
    }

	
	
	protected abstract String[] getKind () ;
	
	
	/**
	 * Return the kind of objects that we as a provider are interested in
	 * 
	 * @return an array of kind of objects that we are interested in
	 */
	
	protected Class[] getKindClass () {
		return null;
	}
	
	
	/**
	 * Return the depth at which discover for new resources will abort if none is found
	 * at the current level and the level below.
	 * 
	 * @return the max depth to search, including current level
	 */
	
	protected int getDepth ( ) {
		return 2;
	}
	
	
	/**
	 * Find candidate resourcesin the container passed. The container is searched
	 * up to a certain depth. If resources are found in that container then the next
	 * level is searched. Otherwise the search at that level stops. So not exactly a full
	 * workspace scan.
	 * 
	 * @param container the container to search (project, folder, workspace root)
	 * @return the list of schemas found
	 * @throws CoreException
	 */
	
	protected List findCandidates ( IContainer container ) throws CoreException {
		
		LinkedList list = new LinkedList();
		
		// Workspace root contains Projects ...
		if (container instanceof IWorkspaceRoot) {
			return findCandidates( container, list, getKind(), getDepth() + 1 );
		}		
		return findCandidates( container, list, getKind(), getDepth() );		
	}
	
	
	
	
	protected List findCandidates ( IContainer container, List list, String [] kind , int depth ) throws CoreException {
		
		if (depth <= 0 || container.isAccessible() == false) {
			return list;
		}
		
		depth -= 1;
		
		boolean bFound = false;
		
		IResource [] rlist = container.members();
			
		for(int i=0; i< rlist.length; i++) {
			
			IResource r = rlist[i];
			if ( r.getType() != IResource.FILE) {
				continue;
			}			
			String name = r.getFileExtension();
			for(int j=0; j < kind.length; j++) {
				if (name != null && name.equalsIgnoreCase(kind[j])) {
					bFound = true;
					list.add( r );
					break;
				}
			}				
		}
		
		// if found some candidates at this level, look only in the next level (and
		// so on). Eventually, not all levels are searched, just the ones that contain
		// xsd resources and their descendant folders
		
		if (bFound) {
			depth = 1;
		}
			
		for(int i=0; i < rlist.length; i++) {
			IResource r = rlist[i];
			if (r instanceof IContainer) {
				findCandidates((IContainer) r, list, kind, depth);
			}
		}
		
		return list;
	}
	
	
}
