/*******************************************************************************
 * Copyright (c) 2006 Sybase, 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:
 *     Sybase, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.jsf.common.ui.internal.dialogs;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jst.jsf.common.ui.JSFUICommonPlugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.eclipse.ui.part.DrillDownComposite;

/**
 * This is a base dialog that uses TreeViewer to show selections, subclass needs
 * to provide IContentProvider, ILabelProvider and ViewerFilter for the
 * TreeViewer. Subclass needs to implement isValidSelection(), which valids the
 * selection, and findInputElement() which provides the root element of the
 * tree. Besides, subclass might need to implement getResult() to return a
 * customized result.
 * 
 * @author mengbo
 * 
 */
public abstract class TreeViewerSelectionDialog extends SelectionDialog {
	private static final String DEFAULT_TITLE = JSFUICommonPlugin
			.getResourceString("Dialog.TreeViewerSelectionDialog.DefaultTitle"); //$NON-NLS-1$

	/** Used to tag the image type */
	public static final int STYLE_NONE = 0;

	private static final int STYLE_INFORMATION = 1;

	private static final int STYLE_ERROR = 2;

	private static final int STYLE_WARNING = 3;

	/** Sizi of the TreeViewer composite */
	private static final int SIZING_SELECTION_PANE_HEIGHT = 300;

	private static final int SIZING_SELECTION_PANE_WIDTH = 320;

	private String _title = DEFAULT_TITLE;

	// the seleciton on the treeviewer.
	private static Object[] _selection;

	// providers
	private ITreeContentProvider _contentProvider;

	private ILabelProvider _labelProvider;

	private ViewerFilter _filter;

	/** The validation image */
	private Label _statusImage;

	/** The validation message */
	private Label _statusLabel;

	private String _statusMessage;

	/** The selection tree */
	private TreeViewer _treeViewer;

	private int _style;

	private ViewerSorter _viewerSorter = null;
	private ViewerComparator _viewerComparator = null;
	/**
	 * @param parentShell
	 * @param statusMessage 
	 * @param style 
	 */
	public TreeViewerSelectionDialog(Shell parentShell, String statusMessage,
			int style) {
		super(parentShell);
		_statusMessage = statusMessage;
		_style = style;
		setShellStyle(SWT.CLOSE | SWT.TITLE | SWT.BORDER
				| SWT.APPLICATION_MODAL | SWT.RESIZE);
	}

	/**
	 * Convenience for TreeViewerSelectionDialog(parentShell, statusMessage, SWT.NONE)
	 * 
	 * @param parentShell
	 * @param statusMessage
	 */
	public TreeViewerSelectionDialog(Shell parentShell, String statusMessage) {
		this(parentShell, statusMessage, SWT.NONE);
	}

	public void setTitle(String title) {
		super.setTitle(title);
		_title = title;
	}

	/**
	 * Returns a new drill down viewer for this dialog.
	 * @param parent 
	 */
	protected void createTreeViewer(Composite parent) {
		// Create drill down
		DrillDownComposite drillDown = new DrillDownComposite(parent,
				SWT.BORDER);
		GridData spec = new GridData(GridData.FILL_BOTH);
		spec.widthHint = SIZING_SELECTION_PANE_WIDTH;
		spec.heightHint = SIZING_SELECTION_PANE_HEIGHT;
		drillDown.setLayoutData(spec);
		_treeViewer = new TreeViewer(drillDown, _style);
		drillDown.setChildTree(_treeViewer);
	}

	private void setTreeViewerProviders() {
		_treeViewer.setContentProvider(_contentProvider);
		_treeViewer.setLabelProvider(_labelProvider);
		if (_viewerSorter == null) {
			_viewerSorter = new ViewerSorter();
		}
		_treeViewer.setSorter(_viewerSorter);
		
		// override if not null.. setSorter is discouraged.
		if (_viewerComparator != null)
		{
		    _treeViewer.setComparator(_viewerComparator);
		}
		_treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
					public void selectionChanged(SelectionChangedEvent event) {
						_selection = getSelectedElements((IStructuredSelection) event
								.getSelection());
						updateStatus();
					}
				});
		_treeViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				ISelection selection = event.getSelection();
				if (selection instanceof IStructuredSelection) {
					Object item = ((IStructuredSelection) selection)
							.getFirstElement();
					if (item instanceof IFile) {
						okPressed();
					} else if (_treeViewer.getExpandedState(item)) {
						_treeViewer.collapseToLevel(item, 1);
					} else {
						_treeViewer.expandToLevel(item, 1);
					}
				}
			}
		});
		_treeViewer.setInput(findInputElement());

		if (_filter != null) {
			_treeViewer.addFilter(_filter);
		}
	}

	/**
	 * Creates the contents of the composite.
	 * @param parent 
	 */
	protected void createTreeViewerComposite(Composite parent) {
		Composite treeViewerComposite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		layout.marginWidth = 0;
		treeViewerComposite.setLayout(layout);

		GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
		gridData.horizontalSpan = 2;
		treeViewerComposite.setLayoutData(gridData);
		Label label = new Label(treeViewerComposite, SWT.WRAP);
		label.setText(_title);
		label.setFont(treeViewerComposite.getFont());
		createTreeViewer(treeViewerComposite);
		Dialog.applyDialogFont(treeViewerComposite);
	}

	/**
	 * Sets the selected existing container.
	 * @param selection 
	 */
	public void setSelectedElement(Object[] selection) {
		// Expand to and select the specified container
		if (selection == null) {
			return;
		}

		for (int i = 0; i < selection.length; i++) {
			if (_selection[i] != null) {
				_treeViewer.expandToLevel(_selection[i], 1);

			}
		}
		_treeViewer.setSelection(new StructuredSelection(selection), true);
	}

	/*
	 * (non-Javadoc) Method declared on Dialog.
	 */
	protected Control createDialogArea(Composite parent) {
		Composite area = (Composite) super.createDialogArea(parent);
		GridLayout gridLayout = new GridLayout();
		gridLayout.numColumns = 2;
		area.setLayout(gridLayout);

		// Container treeviewer composite
		createTreeViewerComposite(area);

		_statusImage = createLabel(area);
		_statusImage.setImage(getMessageImage(STYLE_ERROR));
		_statusLabel = createLabel(area);
		// Link to model
		setTreeViewerProviders();

		return dialogArea;
	}

	private Label createLabel(Composite parent) {
		Label label = new Label(parent, SWT.LEFT);
		GridData data = new GridData();
		data.horizontalSpan = 1;
		data.horizontalAlignment = GridData.FILL;
		label.setLayoutData(data);
		label.setText(_statusMessage == null ? "" : _statusMessage); //$NON-NLS-1$
		return label;
	}

	private Object[] getSelectedElements(IStructuredSelection selection) {
		return selection.toArray();
	}

	/**
	 * @param provider
	 *            The _contentProvider to set.
	 */
	public void setContentProvider(ITreeContentProvider provider) {
		_contentProvider = provider;
	}

	/**
	 * @param provider
	 *            The _labelProvider to set.
	 */
	public void setLabelProvider(ILabelProvider provider) {
		_labelProvider = provider;
	}

	/**
	 * @param filter
	 *            The _filter to set.
	 */
	public void setFilter(ViewerFilter filter) {
		this._filter = filter;
	}

	/**
	 * @param sorter
	 *            The _viewerSorter to set.
	 */
	public void setViewerSorter(ViewerSorter sorter) {
		_viewerSorter = sorter;
	}

	/**
	 * Set the viewer comparator.  If not null, it's set after after the
	 * viewer sorter and thus overrides it.
	 * 
	 * @param viewerComparator
	 */
	public void setViewerComparator(ViewerComparator viewerComparator)
    {
        _viewerComparator = viewerComparator;
    }

    /**
	 * @param message
	 */
	public void setStatusMessage(String message) {
		_statusMessage = message;
	}

	/**
	 * Update the status message
	 */
	private void updateStatus() {
		Object selection = _selection;
		if (_selection != null && _selection.length == 1) {
			selection = _selection[0];
		}
		if (isValidSelection(selection)) {
			_statusImage.setVisible(false);
			_statusLabel.setText(""); //$NON-NLS-1$
			getOkButton().setEnabled(true);
		} else {
			_statusImage.setVisible(true);
			_statusImage.setImage(getMessageImage(STYLE_ERROR));
			_statusImage.redraw();
			_statusLabel.setText(_statusMessage);
			getOkButton().setEnabled(false);
		}

	}

	/**
	 * Get the different message according the message type.
	 * @param imageType 
	 * 
	 * @return Image - the message image
	 */
	protected Image getMessageImage(int imageType) {
		switch (imageType) {
		case STYLE_ERROR:
			return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR);
		case STYLE_WARNING:
			return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
		case STYLE_INFORMATION:
			return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);
		default:
			return null;
		}
	}

	/**
	 * The <code>ContainerSelectionDialog</code> implementation of this
	 * <code>Dialog</code> method builds a list of the selected resource
	 * containers for later retrieval by the client and closes this dialog.
	 */
	protected void okPressed() {
		List chosenContainerPathList = new ArrayList();
		if (_selection != null) {
			chosenContainerPathList.addAll(Arrays.asList(_selection));
		}
		setResult(chosenContainerPathList);
		super.okPressed();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite)
	 */
	protected Control createContents(Composite parent) {
		Control control = super.createContents(parent);
		if (_selection != null) {
			this.setSelectedElement(_selection);
		}
		return control;
	}

	/**
	 * @param selection
	 * @return true if selection is valid
	 */
	protected abstract boolean isValidSelection(Object selection);

	/**
	 * Used to set the input element on the tree viewer
	 * @return the input element
	 */
	protected abstract Object findInputElement();
}
