/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.dialogs;

import java.util.ArrayList;
import java.util.Map;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.DialogUtil;
import org.eclipse.ui.internal.ide.dialogs.IElementFilter;
import org.eclipse.ui.internal.ide.dialogs.ResourceTreeAndListGroup;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.model.WorkbenchViewerSorter;

/**
 * The abstract superclass for a typical import wizard's main page.
 * <p>
 * Clients may subclass this page to inherit its common destination resource
 * selection facilities.
 * </p>
 * <p>
 * Subclasses must implement 
 * <ul>
 *   <li><code>createSourceGroup</code></li>
 * </ul>
 * </p>
 * <p>
 * Subclasses may override
 * <ul>
 *   <li><code>allowNewContainerName</code></li>
 * </ul>
 * </p>
 * <p>
 * Subclasses may extend
 * <ul>
 *   <li><code>handleEvent</code></li>
 * </ul>
 * </p>
 */
public abstract class WizardResourceImportPage extends WizardDataTransferPage {
	private IResource currentResourceSelection;

	// initial value stores
	private String initialContainerFieldValue;
	protected java.util.List selectedTypes = new ArrayList();

	// widgets
	private Text containerNameField;
	private Button containerBrowseButton;
	protected ResourceTreeAndListGroup selectionGroup;

	//messages
	private static final String EMPTY_FOLDER_MESSAGE = IDEWorkbenchMessages.getString("WizardImportPage.specifyFolder"); //$NON-NLS-1$
	private static final String INACCESSABLE_FOLDER_MESSAGE = IDEWorkbenchMessages.getString("WizardImportPage.folderMustExist"); //$NON-NLS-1$

/**
 * Creates an import wizard page. If the initial resource selection 
 * contains exactly one container resource then it will be used as the default
 * import destination.
 *
 * @param name the name of the page
 * @param selection the current resource selection
 */
protected WizardResourceImportPage(String name, IStructuredSelection selection) {
	super(name);

	//Initialize to null
	currentResourceSelection = null;
	if (selection.size() == 1){
		Object firstElement = selection.getFirstElement();
		if(firstElement instanceof IAdaptable){
			Object resource = ((IAdaptable) firstElement).getAdapter(IResource.class);
			if(resource != null)
				currentResourceSelection = (IResource) resource;
		}
	}		

	if (currentResourceSelection != null) {
		if (currentResourceSelection.getType() == IResource.FILE)
			currentResourceSelection = currentResourceSelection.getParent();

		if (!currentResourceSelection.isAccessible())
			currentResourceSelection = null;
	}

}
/**
 * The <code>WizardResourceImportPage</code> implementation of this 
 * <code>WizardDataTransferPage</code> method returns <code>true</code>. 
 * Subclasses may override this method.
 */
protected boolean allowNewContainerName() {
	return true;
}
/** (non-Javadoc)
 * Method declared on IDialogPage.
 */
public void createControl(Composite parent) {

	initializeDialogUnits(parent);
	
	Composite composite = new Composite(parent, SWT.NULL);
	composite.setLayout(new GridLayout());
	composite.setLayoutData(new GridData(
		GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
	composite.setSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
	composite.setFont(parent.getFont());

	createSourceGroup(composite);

	createDestinationGroup(composite);

	createOptionsGroup(composite);

	restoreWidgetValues();
	updateWidgetEnablements();
	setPageComplete(determinePageCompletion());

	setControl(composite);
}
/**
 * Creates the import destination specification controls.
 *
 * @param parent the parent control
 */
protected final void createDestinationGroup(Composite parent) {
	// container specification group
	Composite containerGroup = new Composite(parent,SWT.NONE);
	GridLayout layout = new GridLayout();
	layout.numColumns = 3;
	containerGroup.setLayout(layout);
	containerGroup.setLayoutData(new GridData(
		GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
	containerGroup.setFont(parent.getFont());


	// container label
	Label resourcesLabel = new Label(containerGroup,SWT.NONE);
	resourcesLabel.setText(IDEWorkbenchMessages.getString("WizardImportPage.folder")); //$NON-NLS-1$
	resourcesLabel.setFont(parent.getFont());

	// container name entry field
	containerNameField = new Text(containerGroup,SWT.SINGLE|SWT.BORDER);
	containerNameField.addListener(SWT.Modify,this);
	GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
	data.widthHint = SIZING_TEXT_FIELD_WIDTH;
	containerNameField.setLayoutData(data);
	containerNameField.setFont(parent.getFont());

	// container browse button
	containerBrowseButton = new Button(containerGroup,SWT.PUSH);
	containerBrowseButton.setText(IDEWorkbenchMessages.getString("WizardImportPage.browse2")); //$NON-NLS-1$
	containerBrowseButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
	containerBrowseButton.addListener(SWT.Selection,this);
	containerBrowseButton.setFont(parent.getFont());
	setButtonLayoutData(containerBrowseButton);

	initialPopulateContainerField();
}
/**
 *	Create the import source selection widget
 */
protected void createFileSelectionGroup(Composite parent) {
	
	//Just create with a dummy root.
	this.selectionGroup =
		new ResourceTreeAndListGroup(
			parent,
			new FileSystemElement("Dummy", null, true),//$NON-NLS-1$
			getFolderProvider(),
			new WorkbenchLabelProvider(),
			getFileProvider(),
			new WorkbenchLabelProvider(),
			SWT.NONE,
			DialogUtil.inRegularFontMode(parent));

	ICheckStateListener listener = new ICheckStateListener() {
		public void checkStateChanged(CheckStateChangedEvent event) {
			updateWidgetEnablements();
		}
	};

	WorkbenchViewerSorter sorter = new WorkbenchViewerSorter();
	this.selectionGroup.setTreeSorter(sorter);
	this.selectionGroup.setListSorter(sorter);
	this.selectionGroup.addCheckStateListener(listener);

}
/**
 * Creates the import source specification controls.
 * <p>
 * Subclasses must implement this method.
 * </p>
 *
 * @param parent the parent control
 */
protected abstract void createSourceGroup(Composite parent);

/*
 * @see WizardDataTransferPage.getErrorDialogTitle()
 */
protected String getErrorDialogTitle(){
	return IDEWorkbenchMessages.getString("WizardImportPage.errorDialogTitle"); //$NON-NLS-1$
}

/**
 * Returns the path of the container resource specified in the container
 * name entry field, or <code>null</code> if no name has been typed in.
 * <p>
 * The container specified by the full path might not exist and would need to
 * be created.
 * </p>
 *
 * @return the full path of the container resource specified in
 *   the container name entry field, or <code>null</code>
 */
protected IPath getContainerFullPath() {
	IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();

	//make the path absolute to allow for optional leading slash
	IPath testPath = getResourcePath();
	
	if(testPath.equals(workspace.getRoot().getFullPath()))
		return testPath;

	IStatus result =
		workspace.validatePath(
			testPath.toString(),
			IResource.PROJECT | IResource.FOLDER | IResource.ROOT);
	if (result.isOK()) {
		return testPath;
	}

	return null;
}
/**
 * Returns a content provider for <code>FileSystemElement</code>s that returns 
 * only files as children.
 */
protected abstract ITreeContentProvider getFileProvider();
/**
 * Returns a content provider for <code>FileSystemElement</code>s that returns 
 * only folders as children.
 */
protected abstract ITreeContentProvider getFolderProvider();
/**
 * Return the path for the resource field.
 * @return IPath
 */
protected IPath getResourcePath() {
	return getPathFromText(this.containerNameField);
}
/**
 * Returns this page's list of currently-specified resources to be 
 * imported. This is the primary resource selection facility accessor for 
 * subclasses.
 *
 * @return a list of resources currently selected 
 * for export (element type: <code>IResource</code>)
 */
protected java.util.List getSelectedResources() {
	return this.selectionGroup.getAllCheckedListItems();
}

/**
 * Returns this page's list of currently-specified resources to be 
 * imported filtered by the IElementFilter.
 *
 */
protected void getSelectedResources(IElementFilter filter, IProgressMonitor monitor) throws InterruptedException{
	this.selectionGroup.getAllCheckedListItems(filter,monitor);
}

/**
 * Returns the container resource specified in the container name entry field,
 * or <code>null</code> if such a container does not exist in the workbench.
 *
 * @return the container resource specified in the container name entry field,
 *   or <code>null</code>
 */
protected IContainer getSpecifiedContainer() {
	IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
	IPath path = getContainerFullPath();
	if (workspace.getRoot().exists(path))
		return (IContainer) workspace.getRoot().findMember(path);

	return null;
}
/**
 * Returns a collection of the currently-specified resource types for
 * use by the type selection dialog.
 */
protected java.util.List getTypesToImport() {

	return selectedTypes;
}
/**
 * Opens a container selection dialog and displays the user's subsequent
 * container resource selection in this page's container name field.
 */
protected void handleContainerBrowseButtonPressed() {
	// see if the user wishes to modify this container selection
	IPath containerPath =
		queryForContainer(
			getSpecifiedContainer(), 
			IDEWorkbenchMessages.getString("WizardImportPage.selectFolderLabel"), //$NON-NLS-1$
			IDEWorkbenchMessages.getString("WizardImportPage.selectFolderTitle")); //$NON-NLS-1$

	// if a container was selected then put its name in the container name field
	if (containerPath != null) { // null means user cancelled
		setErrorMessage(null);
		containerNameField.setText(containerPath.makeRelative().toString());
	}
}
/**
 * The <code>WizardResourceImportPage</code> implementation of this 
 * <code>Listener</code> method handles all events and enablements for controls
 * on this page. Subclasses may extend.
 * @param event Event
 */
public void handleEvent(Event event) {
	Widget source = event.widget;

	if (source == containerBrowseButton)
		handleContainerBrowseButtonPressed();

	updateWidgetEnablements();
}
/**
 *	Open a registered type selection dialog and note the selections
 *	in the receivers types-to-export field
 */
protected void handleTypesEditButtonPressed() {

	TypeFilteringDialog dialog =
		new TypeFilteringDialog(getContainer().getShell(), getTypesToImport());

	dialog.open();

	Object[] newSelectedTypes = dialog.getResult();
	if (newSelectedTypes != null) { // ie.- did not press Cancel
		this.selectedTypes = new ArrayList(newSelectedTypes.length);
		for (int i = 0; i < newSelectedTypes.length; i++)
			this.selectedTypes.add(newSelectedTypes[i]);

		setupSelectionsBasedOnSelectedTypes();
	}

}
/**
 * Sets the initial contents of the container name field.
 */
protected final void initialPopulateContainerField() {
	if (initialContainerFieldValue != null)
		containerNameField.setText(initialContainerFieldValue);
	else if (currentResourceSelection != null)
		containerNameField.setText(currentResourceSelection.getFullPath().makeRelative().toString());
}
/**
 * Set all of the selections in the selection group to value
 * @param value boolean
 */
protected void setAllSelections(boolean value) {
	selectionGroup.setAllSelections(value);
}
/**
 * Sets the value of this page's container resource field, or stores
 * it for future use if this page's controls do not exist yet.
 *
 * @param value String
 */
public void setContainerFieldValue(String value) {
	if (containerNameField == null)
		initialContainerFieldValue = value;
	else
		containerNameField.setText(value);
}
/**
 * Update the tree to only select those elements that match the selected types.
 * Do nothing by default.
 */
protected void setupSelectionsBasedOnSelectedTypes() {
}
/**
 * Update the selections with those in map .
 * @param map Map - key tree elements, values Lists of list elements
 */
protected void updateSelections(final Map map) {
	
	Runnable runnable  = new Runnable() {
		public void run(){
			selectionGroup.updateSelections(map);
		}
	};
		
	BusyIndicator.showWhile(getShell().getDisplay(),runnable);
}
/**
 * Check if widgets are enabled or disabled by a change in the dialog.
 */
protected void updateWidgetEnablements() {

	boolean pageComplete = determinePageCompletion();
	setPageComplete(pageComplete);
	if (pageComplete)
		setMessage(null);
	super.updateWidgetEnablements();
}
/* (non-Javadoc)
 * Method declared on WizardDataTransferPage.
 */
protected final boolean validateDestinationGroup() {

	IPath containerPath = getContainerFullPath();
	if (containerPath == null) {
		setMessage(EMPTY_FOLDER_MESSAGE);
		return false;
	}

	// If the container exist, validate it
	IContainer container = getSpecifiedContainer();
	if (container == null) {
		//if it is does not exist be sure the project does
		IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
		IPath projectPath =
			containerPath.removeLastSegments(containerPath.segmentCount() - 1);

		if (workspace.getRoot().exists(projectPath))
			return true;
		else {
			setErrorMessage(IDEWorkbenchMessages.getString("WizardImportPage.projectNotExist")); //$NON-NLS-1$
			return false;
		}
	} else {
		if (!container.isAccessible()) {
			setErrorMessage(INACCESSABLE_FOLDER_MESSAGE);
			return false;
		}
		if (container.getLocation() == null) {
			if (container.isLinked()) {
				setErrorMessage(IDEWorkbenchMessages.getString("WizardImportPage.undefinedPathVariable")); //$NON-NLS-1$	
			}
			else {
				setErrorMessage(IDEWorkbenchMessages.getString("WizardImportPage.containerNotExist")); //$NON-NLS-1$
			}
			return false;
		}
	}
	
	if(sourceConflictsWithDestination(containerPath)){
		setErrorMessage(getSourceConflictMessage());
		return false;
	}
			
	return true;

}

/**
 * Returns the error message for when the source conflicts
 * with the destination.
 */
protected final String getSourceConflictMessage(){
	return(
		IDEWorkbenchMessages.getString(
			"WizardImportPage.importOnReceiver")); //$NON-NLS-1$
}


/**
 * Returns whether or not the source location conflicts
 * with the destination resource. By default this is not
 * checked, so <code>false</code> is returned.
 * 
 * @param sourcePath the path being checked
 * @return <code>true</code> if the source location conflicts with the
 *   destination resource, <code>false</code> if not
 */
protected boolean sourceConflictsWithDestination(IPath sourcePath){
	return false;
}

/*
 * @see WizardDataTransferPage.determinePageCompletion.
 */
protected boolean determinePageCompletion() {
	//Check for valid projects before making the user do anything 
	if(noOpenProjects()){
		setErrorMessage(IDEWorkbenchMessages.getString("WizardImportPage.noOpenProjects")); //$NON-NLS-1$
		return false;
	}
	return super.determinePageCompletion();
}	

/**
 * Returns whether or not the passed workspace has any 
 * open projects
 * @return boolean
 */
private boolean noOpenProjects(){
	IProject[] projects = IDEWorkbenchPlugin.getPluginWorkspace().getRoot().getProjects();
	for(int i = 0; i < projects.length; i ++){
		if(projects[i].isOpen())
			return false;
	}
	return true;
}
}
