/*******************************************************************************
 * Copyright (c) 2003 Berthold Daum.
 * 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:
 *     Berthold Daum
 *******************************************************************************/
package net.sourceforge.phpeclipse.ui.overlaypages;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jface.preference.IPreferenceNode;
import org.eclipse.jface.preference.IPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.ui.dialogs.PropertyPage;
import org.eclipse.ui.part.PageBook;

/**
 * @author Berthold Daum
 */
public abstract class OverlayPage extends PropertyPage {

	/*** Name of resource property for the selection of workbench or project settings ***/
	public static final String USEPROJECTSETTINGS = "useProjectSettings"; //$NON-NLS-1$
	
	private static final String FALSE = "false"; //$NON-NLS-1$
	private static final String TRUE = "true"; //$NON-NLS-1$

	// Additional buttons for property pages
	private Button useWorkspaceSettingsButton,
		useProjectSettingsButton,
		configureButton;

	// Overlay preference store for property pages
	private PropertyStore overlayStore;

	// The image descriptor of this pages title image
	private ImageDescriptor image;

	// Cache for page id
	private String pageId;

	// Container for subclass controls
	private Composite contents;

	/**
	 * Constructor
	 */
	public OverlayPage() {
		super();
	}

	/**
	 * Constructor
	 * @param title - title string
	 */
	public OverlayPage(String title) {
		super();
		setTitle(title);
	}

	/**
	 * Constructor
	 * @param title - title string
	 * @param image - title image
	 */
	public OverlayPage(String title, ImageDescriptor image) {
		super();
		setTitle(title);
		setImageDescriptor(image);
		this.image = image;
	}

	/**
	 * Returns the id of the current preference page as defined in plugin.xml
	 * Subclasses must implement. 
	 * 
	 * @return - the qualifier
	 */
	protected abstract String getPageId();

	/**
	 * Returns true if this instance represents a property page
	 * @return - true for property pages, false for preference pages
	 */
	public boolean isPropertyPage() {
		return getElement() != null;
	}

	/**
	 *  We need to implement createContents method. In case of property pages we insert two radio buttons
	 * and a push button at the top of the page. Below this group we create a new composite for the contents
	 * created by subclasses.
	 * 
	 * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
	 */
	protected Control createContents(Composite parent) {
		if (isPropertyPage())
			createSelectionGroup(parent);
		contents = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		layout.marginHeight = 0;
		layout.marginWidth = 0;
		contents.setLayout(layout);
		contents.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		return contents;
	}

	/**
	 * Creates and initializes a selection group with two choice buttons and one push button.
	 * @param parent - the parent composite
	 */
	private void createSelectionGroup(Composite parent) {
		Composite comp = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout(2, false);
		layout.marginHeight = 0;
		layout.marginWidth = 0;
		comp.setLayout(layout);
		comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		Composite radioGroup = new Composite(comp, SWT.NONE);
		radioGroup.setLayout(new GridLayout());
		radioGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		useWorkspaceSettingsButton = createRadioButton(radioGroup, Messages.getString("OverlayPage.Use_Workspace_Settings")); //$NON-NLS-1$
		useProjectSettingsButton = createRadioButton(radioGroup, Messages.getString("OverlayPage.Use_Project_Settings")); //$NON-NLS-1$
		configureButton = new Button(comp, SWT.PUSH);
		configureButton.setText(Messages.getString("OverlayPage.Configure_Workspace_Settings")); //$NON-NLS-1$
		configureButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				configureWorkspaceSettings();
			}
		});
		// Set workspace/project radio buttons
		try {
			String use =
				((IResource) getElement()).getPersistentProperty(
					new QualifiedName(pageId, USEPROJECTSETTINGS));
			if (TRUE.equals(use)) {
				useProjectSettingsButton.setSelection(true);
				configureButton.setEnabled(false);
			} else
				useWorkspaceSettingsButton.setSelection(true);
		} catch (CoreException e) {
			useWorkspaceSettingsButton.setSelection(true);
		}
	}

	/**
	 * Convenience method creating a radio button
	 * @param parent - the parent composite
	 * @param label - the button label
	 * @return - the new button
	 */
	private Button createRadioButton(Composite parent, String label) {
		final Button button = new Button(parent, SWT.RADIO);
		button.setText(label);
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				configureButton.setEnabled(
					button == useWorkspaceSettingsButton);
				setControlsEnabled();
			}
		});
		return button;
	}

	/**
	 * In case of property pages we create a new PropertyStore as local overlay store.
	 * After all controls have been create, we enable/disable these controls
	 * 
	 * @see org.eclipse.jface.preference.PreferencePage#createControl()
	 */
	public void createControl(Composite parent) {
		// Special treatment for property pages
		if (isPropertyPage()) {
			// Cache the page id
			pageId = getPageId();
			// Create an overlay preference store and fill it with properties
			overlayStore =
				new PropertyStore(
					(IResource) getElement(),
					super.getPreferenceStore(),
					pageId);
			// Set overlay store as current preference store
		}
		super.createControl(parent);
		// Update enablement of all subclass controls
		if (isPropertyPage())
			setControlsEnabled();
	}

	/* 
	 * Returns in case of property pages the overlay store - otherwise the standard preference store
	 * @see org.eclipse.jface.preference.PreferencePage#getPreferenceStore()
	 */
	public IPreferenceStore getPreferenceStore() {
		if (isPropertyPage())
			return overlayStore;
		return super.getPreferenceStore();
	}

	/**
	 * Enables or disables the controls of this page
	 */
	private void setControlsEnabled() {
		boolean enabled = useProjectSettingsButton.getSelection();
		setControlsEnabled(enabled);
	}

	/**
	 * Enables or disables the controls of this page
	 * Subclasses may override.
	 * 
	 * @param enabled - true if controls shall be enabled
	 */
	protected void setControlsEnabled(boolean enabled) {
		setControlsEnabled(contents, enabled);
	}

	/**
	 * Enables or disables a tree of controls starting at the specified root. 
	 * We spare tabbed notebooks and pagebooks to allow for user navigation.
	 * 
	 * @param root - the root composite
	 * @param enabled - true if controls shall be enabled
	 */
	private void setControlsEnabled(Composite root, boolean enabled) {
		Control[] children = root.getChildren();
		for (int i = 0; i < children.length; i++) {
			Control child = children[i];
			if (!(child instanceof CTabFolder) && !(child instanceof TabFolder) && !(child instanceof PageBook))
				child.setEnabled(enabled);
			if (child instanceof Composite)
				setControlsEnabled((Composite) child, enabled);
		}
	}

	/** 
	 * We override the performOk method. In case of property pages 
	 * we save the state of the radio buttons.
	 * 
	 * @see org.eclipse.jface.preference.IPreferencePage#performOk()
	 */
	public boolean performOk() {
		boolean result = super.performOk();
		if (result && isPropertyPage()) {
			// Save state of radiobuttons in project properties
			IResource resource = (IResource) getElement();
			try {
				String value =
					(useProjectSettingsButton.getSelection()) ? TRUE : FALSE;
				resource.setPersistentProperty(
					new QualifiedName(pageId, USEPROJECTSETTINGS),
					value);
			} catch (CoreException e) {
			}
		}
		return result;
	}

	/**
	 * We override the performDefaults method. In case of property pages we
	 * switch back to the workspace settings and disable the page controls.
	 * 
	 * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
	 */
	protected void performDefaults() {
		if (isPropertyPage()) {
			useWorkspaceSettingsButton.setSelection(true);
			useProjectSettingsButton.setSelection(false);
			configureButton.setEnabled(true);
			setControlsEnabled();
		}
		super.performDefaults();
	}

	/**
	 * Creates a new preferences page and opens it
	 * @see com.bdaum.SpellChecker.preferences.SpellCheckerPreferencePage#configureWorkspaceSettings()
	 */
	protected void configureWorkspaceSettings() {
		try {
			// create a new instance of the current class
			IPreferencePage page =
				(IPreferencePage) this.getClass().newInstance();
			page.setTitle(getTitle());
			page.setImageDescriptor(image);
			// and show it
			showPreferencePage(pageId, page);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Show a single preference pages
	 * @param id - the preference page identification
	 * @param page - the preference page
	 */
	protected void showPreferencePage(String id, IPreferencePage page) {
		final IPreferenceNode targetNode = new PreferenceNode(id, page);
		PreferenceManager manager = new PreferenceManager();
		manager.addToRoot(targetNode);
		final PreferenceDialog dialog =
			new PreferenceDialog(getControl().getShell(), manager);
		BusyIndicator.showWhile(getControl().getDisplay(), new Runnable() {
			public void run() {
				dialog.create();
				dialog.setMessage(targetNode.getLabelText());
				dialog.open();
			}
		});
	}

}