/*******************************************************************************
 * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.ui.wizards;

import net.sourceforge.phpdt.core.IJavaElement;
import net.sourceforge.phpdt.core.IJavaProject;
import net.sourceforge.phpdt.core.IPackageFragmentRoot;
import net.sourceforge.phpdt.core.JavaCore;
import net.sourceforge.phpdt.core.JavaModelException;
import net.sourceforge.phpdt.externaltools.internal.ui.StatusInfo;
import net.sourceforge.phpdt.internal.ui.viewsupport.IViewPartInputProvider;
import net.sourceforge.phpdt.internal.ui.wizards.NewWizardMessages;
import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.DialogField;
import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.IStringButtonAdapter;
import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.LayoutUtil;
import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.StringButtonDialogField;
import net.sourceforge.phpeclipse.PHPeclipsePlugin;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.views.contentoutline.ContentOutline;

/**
 * Wizard page that acts as a base class for wizard pages that create new Java
 * elements. The class provides a input field for source folders (called
 * container in this class) and API to validate the enter source folder name.
 * 
 * @since 2.0
 */
public abstract class NewContainerWizardPage extends NewElementWizardPage {

	/** Id of the container field */
	protected static final String CONTAINER = "NewContainerWizardPage.container"; //$NON-NLS-1$

	/** The status of the last validation. */
	protected IStatus fContainerStatus;

	private StringButtonDialogField fContainerDialogField;

	/*
	 * package fragment root corresponding to the input type (can be null)
	 */
	private IPackageFragmentRoot fCurrRoot;

	private IWorkspaceRoot fWorkspaceRoot;

	/**
	 * Create a new <code>NewContainerWizardPage</code>
	 * 
	 * @param name
	 *            the wizard page's name
	 */
	public NewContainerWizardPage(String name) {
		super(name);
		fWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
		ContainerFieldAdapter adapter = new ContainerFieldAdapter();

		fContainerDialogField = new StringButtonDialogField(adapter);
		fContainerDialogField.setDialogFieldListener(adapter);
		fContainerDialogField.setLabelText(NewWizardMessages
				.getString("NewContainerWizardPage.container.label")); //$NON-NLS-1$
		fContainerDialogField.setButtonLabel(NewWizardMessages
				.getString("NewContainerWizardPage.container.button")); //$NON-NLS-1$

		fContainerStatus = new StatusInfo();
		fCurrRoot = null;
	}

	/**
	 * Initializes the source folder field with a valid package fragement root.
	 * The package fragement root is computed from the given Java element.
	 * 
	 * @param elem
	 *            the Java element used to compute the initial package fragment
	 *            root used as the source folder
	 */
	protected void initContainerPage(IJavaElement elem) {
		IPackageFragmentRoot initRoot = null;
		// if (elem != null) {
		// initRoot= JavaModelUtil.getPackageFragmentRoot(elem);
		// if (initRoot == null || initRoot.isArchive()) {
		// IJavaProject jproject= elem.getJavaProject();
		// if (jproject != null) {
		// try {
		// initRoot= null;
		// if (jproject.exists()) {
		// IPackageFragmentRoot[] roots= jproject.getPackageFragmentRoots();
		// for (int i= 0; i < roots.length; i++) {
		// if (roots[i].getKind() == IPackageFragmentRoot.K_SOURCE) {
		// initRoot= roots[i];
		// break;
		// }
		// }
		// }
		// } catch (JavaModelException e) {
		// PHPeclipsePlugin.log(e);
		// }
		// if (initRoot == null) {
		// initRoot= jproject.getPackageFragmentRoot(jproject.getResource());
		// }
		// }
		// }
		// }
		// setPackageFragmentRoot(initRoot, true);
	}

	/**
	 * Utility method to inspect a selection to find a Java element.
	 * 
	 * @param selection
	 *            the selection to be inspected
	 * @return a Java element to be used as the initial selection, or
	 *         <code>null</code>, if no Java element exists in the given
	 *         selection
	 */
	protected IJavaElement getInitialJavaElement(IStructuredSelection selection) {
		IJavaElement jelem = null;
		if (selection != null && !selection.isEmpty()) {
			Object selectedElement = selection.getFirstElement();
			if (selectedElement instanceof IAdaptable) {
				IAdaptable adaptable = (IAdaptable) selectedElement;

				jelem = (IJavaElement) adaptable.getAdapter(IJavaElement.class);
				if (jelem == null) {
					IResource resource = (IResource) adaptable
							.getAdapter(IResource.class);
					if (resource != null
							&& resource.getType() != IResource.ROOT) {
						while (jelem == null
								&& resource.getType() != IResource.PROJECT) {
							resource = resource.getParent();
							jelem = (IJavaElement) resource
									.getAdapter(IJavaElement.class);
						}
						if (jelem == null) {
							jelem = JavaCore.create(resource); // java project
						}
					}
				}
			}
		}
		if (jelem == null) {
			IWorkbenchPart part = PHPeclipsePlugin.getActivePage()
					.getActivePart();
			if (part instanceof ContentOutline) {
				part = PHPeclipsePlugin.getActivePage().getActiveEditor();
			}

			if (part instanceof IViewPartInputProvider) {
				Object elem = ((IViewPartInputProvider) part)
						.getViewPartInput();
				if (elem instanceof IJavaElement) {
					jelem = (IJavaElement) elem;
				}
			}
		}

		if (jelem == null || jelem.getElementType() == IJavaElement.JAVA_MODEL) {
			try {
				IJavaProject[] projects = JavaCore.create(getWorkspaceRoot())
						.getJavaProjects();
				if (projects.length == 1) {
					jelem = projects[0];
				}
			} catch (JavaModelException e) {
				PHPeclipsePlugin.log(e);
			}
		}
		return jelem;
	}

	/**
	 * Returns the recommended maximum width for text fields (in pixels). This
	 * method requires that createContent has been called before this method is
	 * call. Subclasses may override to change the maximum width for text
	 * fields.
	 * 
	 * @return the recommended maximum width for text fields.
	 */
	protected int getMaxFieldWidth() {
		return convertWidthInCharsToPixels(40);
	}

	/**
	 * Creates the necessary controls (label, text field and browse button) to
	 * edit the source folder location. The method expects that the parent
	 * composite uses a <code>GridLayout</code> as its layout manager and that
	 * the grid layout has at least 3 columns.
	 * 
	 * @param parent
	 *            the parent composite
	 * @param nColumns
	 *            the number of columns to span. This number must be greater or
	 *            equal three
	 */
	protected void createContainerControls(Composite parent, int nColumns) {
		fContainerDialogField.doFillIntoGrid(parent, nColumns);
		LayoutUtil.setWidthHint(fContainerDialogField.getTextControl(null),
				getMaxFieldWidth());
	}

	/**
	 * Sets the focus to the source folder's text field.
	 */
	protected void setFocusOnContainer() {
		fContainerDialogField.setFocus();
	}

	// -------- ContainerFieldAdapter --------

	private class ContainerFieldAdapter implements IStringButtonAdapter,
			IDialogFieldListener {

		// -------- IStringButtonAdapter
		public void changeControlPressed(DialogField field) {
			containerChangeControlPressed(field);
		}

		// -------- IDialogFieldListener
		public void dialogFieldChanged(DialogField field) {
			containerDialogFieldChanged(field);
		}
	}

	private void containerChangeControlPressed(DialogField field) {
		// take the current jproject as init element of the dialog
		// IPackageFragmentRoot root= getPackageFragmentRoot();
		// root= chooseSourceContainer(root);
		// if (root != null) {
		// setPackageFragmentRoot(root, true);
		// }
	}

	private void containerDialogFieldChanged(DialogField field) {
		if (field == fContainerDialogField) {
			fContainerStatus = containerChanged();
		}
		// tell all others
		handleFieldChanged(CONTAINER);
	}

	// ----------- validation ----------

	/**
	 * This method is a hook which gets called after the source folder's text
	 * input field has changed. This default implementation updates the model
	 * and returns an error status. The underlying model is only valid if the
	 * returned status is OK.
	 * 
	 * @return the model's error status
	 */
	protected IStatus containerChanged() {
		StatusInfo status = new StatusInfo();

		fCurrRoot = null;
		String str = getPackageFragmentRootText();
		if (str.length() == 0) {
			status
					.setError(NewWizardMessages
							.getString("NewContainerWizardPage.error.EnterContainerName")); //$NON-NLS-1$
			return status;
		}
		IPath path = new Path(str);
		IResource res = fWorkspaceRoot.findMember(path);
		if (res != null) {
			int resType = res.getType();
			if (resType == IResource.PROJECT || resType == IResource.FOLDER) {
				IProject proj = res.getProject();
				if (!proj.isOpen()) {
					status
							.setError(NewWizardMessages
									.getFormattedString(
											"NewContainerWizardPage.error.ProjectClosed", proj.getFullPath().toString())); //$NON-NLS-1$
					return status;
				}
				IJavaProject jproject = JavaCore.create(proj);
				// fCurrRoot= jproject.getPackageFragmentRoot(res);
				// if (res.exists()) {
				// try {
				// if (!proj.hasNature(JavaCore.NATURE_ID)) {
				// if (resType == IResource.PROJECT) {
				// status.setError(NewWizardMessages.getString("NewContainerWizardPage.warning.NotAJavaProject"));
				// //$NON-NLS-1$
				// } else {
				// status.setWarning(NewWizardMessages.getString("NewContainerWizardPage.warning.NotInAJavaProject"));
				// //$NON-NLS-1$
				// }
				// return status;
				// }
				// } catch (CoreException e) {
				// status.setWarning(NewWizardMessages.getString("NewContainerWizardPage.warning.NotAJavaProject"));
				// //$NON-NLS-1$
				// }
				// if (!jproject.isOnClasspath(fCurrRoot)) {
				// status.setWarning(NewWizardMessages.getFormattedString("NewContainerWizardPage.warning.NotOnClassPath",
				// str)); //$NON-NLS-1$
				// }
				// if (fCurrRoot.isArchive()) {
				// status.setError(NewWizardMessages.getFormattedString("NewContainerWizardPage.error.ContainerIsBinary",
				// str)); //$NON-NLS-1$
				// return status;
				// }
				// }
				return status;
			} else {
				status.setError(NewWizardMessages.getFormattedString(
						"NewContainerWizardPage.error.NotAFolder", str)); //$NON-NLS-1$
				return status;
			}
		} else {
			status.setError(NewWizardMessages.getFormattedString(
					"NewContainerWizardPage.error.ContainerDoesNotExist", str)); //$NON-NLS-1$
			return status;
		}
	}

	// -------- update message ----------------

	/**
	 * Hook method that gets called when a field on this page has changed. For
	 * this page the method gets called when the source folder field changes.
	 * <p>
	 * Every sub type is responsible to call this method when a field on its
	 * page has changed. Subtypes override (extend) the method to add
	 * verification when a own field has a dependency to an other field. For
	 * example the class name input must be verified again when the package
	 * field changes (check for duplicated class names).
	 * 
	 * @param fieldName
	 *            The name of the field that has changed (field id). For the
	 *            source folder the field id is <code>CONTAINER</code>
	 */
	protected void handleFieldChanged(String fieldName) {
	}

	// ---- get ----------------

	/**
	 * Returns the workspace root.
	 * 
	 * @return the workspace root
	 */
	protected IWorkspaceRoot getWorkspaceRoot() {
		return fWorkspaceRoot;
	}

	/**
	 * Returns the <code>IPackageFragmentRoot</code> that corresponds to the
	 * current value of the source folder field.
	 * 
	 * @return the IPackageFragmentRoot or <code>null</code> if the current
	 *         source folder value is not a valid package fragment root
	 * 
	 */
	public IPackageFragmentRoot getPackageFragmentRoot() {
		return fCurrRoot;
	}

	/**
	 * Returns the current text of source folder text field.
	 * 
	 * @return the text of the source folder text field
	 */
	public String getPackageFragmentRootText() {
		return fContainerDialogField.getText();
	}

	/**
	 * Sets the current source folder (model and text field) to the given
	 * package fragment root.
	 * 
	 * @param canBeModified
	 *            if <code>false</code> the source folder field can not be
	 *            changed by the user. If <code>true</code> the field is
	 *            editable
	 */
	// public void setPackageFragmentRoot(IPackageFragmentRoot root, boolean
	// canBeModified) {
	// fCurrRoot= root;
	// String str= (root == null) ? "" :
	// root.getPath().makeRelative().toString(); //$NON-NLS-1$
	// fContainerDialogField.setText(str);
	// fContainerDialogField.setEnabled(canBeModified);
	// }
	// ------------- choose source container dialog
	// private IPackageFragmentRoot chooseSourceContainer(IJavaElement
	// initElement) {
	// Class[] acceptedClasses= new Class[] { IPackageFragmentRoot.class,
	// IJavaProject.class };
	// TypedElementSelectionValidator validator= new
	// TypedElementSelectionValidator(acceptedClasses, false) {
	// public boolean isSelectedValid(Object element) {
	// try {
	// if (element instanceof IJavaProject) {
	// IJavaProject jproject= (IJavaProject)element;
	// IPath path= jproject.getProject().getFullPath();
	// return (jproject.findPackageFragmentRoot(path) != null);
	// } else if (element instanceof IPackageFragmentRoot) {
	// return (((IPackageFragmentRoot)element).getKind() ==
	// IPackageFragmentRoot.K_SOURCE);
	// }
	// return true;
	// } catch (JavaModelException e) {
	// PHPeclipsePlugin.log(e.getStatus()); // just log, no ui in validation
	// }
	// return false;
	// }
	// };
	//		
	// acceptedClasses= new Class[] { IJavaModel.class,
	// IPackageFragmentRoot.class, IJavaProject.class };
	// ViewerFilter filter= new TypedViewerFilter(acceptedClasses) {
	// public boolean select(Viewer viewer, Object parent, Object element) {
	// if (element instanceof IPackageFragmentRoot) {
	// try {
	// return (((IPackageFragmentRoot)element).getKind() ==
	// IPackageFragmentRoot.K_SOURCE);
	// } catch (JavaModelException e) {
	// PHPeclipsePlugin.log(e.getStatus()); // just log, no ui in validation
	// return false;
	// }
	// }
	// return super.select(viewer, parent, element);
	// }
	// };
	//
	// StandardJavaElementContentProvider provider= new
	// StandardJavaElementContentProvider();
	// ILabelProvider labelProvider= new
	// JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT);
	// ElementTreeSelectionDialog dialog= new
	// ElementTreeSelectionDialog(getShell(), labelProvider, provider);
	// dialog.setValidator(validator);
	// dialog.setSorter(new JavaElementSorter());
	// dialog.setTitle(NewWizardMessages.getString("NewContainerWizardPage.ChooseSourceContainerDialog.title"));
	// //$NON-NLS-1$
	// dialog.setMessage(NewWizardMessages.getString("NewContainerWizardPage.ChooseSourceContainerDialog.description"));
	// //$NON-NLS-1$
	// dialog.addFilter(filter);
	// dialog.setInput(JavaCore.create(fWorkspaceRoot));
	// dialog.setInitialSelection(initElement);
	//		
	// if (dialog.open() == ElementTreeSelectionDialog.OK) {
	// Object element= dialog.getFirstResult();
	// if (element instanceof IJavaProject) {
	// IJavaProject jproject= (IJavaProject)element;
	// return jproject.getPackageFragmentRoot(jproject.getProject());
	// } else if (element instanceof IPackageFragmentRoot) {
	// return (IPackageFragmentRoot)element;
	// }
	// return null;
	// }
	// return null;
	// }
}