/*******************************************************************************
 * 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.overlaypages;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
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.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.ui.IWorkbenchPropertyPage;
/**
 * @author Berthold Daum
 */
public abstract class FieldEditorOverlayPage extends FieldEditorPreferencePage
    implements
      IWorkbenchPropertyPage {
  /**
   * * 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$
  private boolean fUseFileSettings = false;
  // Stores all created field editors
  private List editors = new ArrayList();
  // Stores owning element of properties
  private IAdaptable element;
  // Additional buttons for property pages
  private Button useWorkspaceSettingsButton, useProjectSettingsButton,
      configureButton;
  // Overlay preference store for property pages
  private IPreferenceStore overlayStore;
  // The image descriptor of this pages title image
  private ImageDescriptor image;
  // Cache for page id
  private String pageId;
  /**
   * Constructor
   * 
   * @param style -
   *            layout style
   */
  public FieldEditorOverlayPage(int style) {
    super(style);
  }
  /**
   * Constructor
   * 
   * @param style -
   *            layout style
   */
  public FieldEditorOverlayPage(int style, boolean isFileSettings) {
    super(style);
    fUseFileSettings = isFileSettings;
  }
  /**
   * Constructor
   * 
   * @param title -
   *            title string
   * @param style -
   *            layout style
   */
  public FieldEditorOverlayPage(String title, int style) {
    super(title, style);
  }
  public FieldEditorOverlayPage(String title, int style, boolean isFileSettings) {
    super(title, style);
    fUseFileSettings = isFileSettings;
  }
  /**
   * Constructor
   * 
   * @param title -
   *            title string
   * @param image -
   *            title image
   * @param style -
   *            layout style
   */
  public FieldEditorOverlayPage(String title, ImageDescriptor image, int style) {
    super(title, image, style);
    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();
  /**
   * Receives the object that owns the properties shown in this property page.
   * 
   * @see org.eclipse.ui.IWorkbenchPropertyPage#setElement(org.eclipse.core.runtime.IAdaptable)
   */
  public void setElement(IAdaptable element) {
    this.element = element;
  }
  /**
   * Delivers the object that owns the properties shown in this property page.
   * 
   * @see org.eclipse.ui.IWorkbenchPropertyPage#getElement()
   */
  public IAdaptable getElement() {
    return element;
  }
  /**
   * 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 override the addField method. This allows us to store each field editor
   * added by subclasses in a list for later processing.
   * 
   * @see org.eclipse.jface.preference.FieldEditorPreferencePage#addField(org.eclipse.jface.preference.FieldEditor)
   */
  protected void addField(FieldEditor editor) {
    editors.add(editor);
    super.addField(editor);
  }
  /**
   * We override the createControl method. In case of property pages we create
   * a new PropertyStore as local preference store. After all control 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 state of all subclass controls
    if (isPropertyPage())
      updateFieldEditors();
  }
  /**
   * We override the createContents method. In case of property pages we insert
   * two radio buttons at the top of the page.
   * 
   * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
   */
  protected Control createContents(Composite parent) {
    if (isPropertyPage())
      createSelectionGroup(parent);
    return super.createContents(parent);
  }
  /**
   * 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$
    if (fUseFileSettings) {
      useProjectSettingsButton = createRadioButton(radioGroup, Messages
          .getString("OverlayPage.Use_File_Settings")); //$NON-NLS-1$
    } else {
      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);
        updateFieldEditors();
      }
    });
    return button;
  }
  /**
   * Returns in case of property pages the overlay store, in case of preference
   * pages 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 field editors and buttons of this page
   */
  private void updateFieldEditors() {
    // We iterate through all field editors
    boolean enabled = useProjectSettingsButton.getSelection();
    updateFieldEditors(enabled);
  }
  /**
   * Enables or disables the field editors and buttons of this page Subclasses
   * may override.
   * 
   * @param enabled -
   *            true if enabled
   */
  protected void updateFieldEditors(boolean enabled) {
    Composite parent = getFieldEditorParent();
    Iterator it = editors.iterator();
    while (it.hasNext()) {
      FieldEditor editor = (FieldEditor) it.next();
      editor.setEnabled(enabled, parent);
    }
  }
  /**
   * We override the performOk method. In case of property pages we copy the
   * values in the overlay store into the property values of the selected
   * project. We also 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 field editors.
   * 
   * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
   */
  protected void performDefaults() {
    if (isPropertyPage()) {
      useWorkspaceSettingsButton.setSelection(true);
      useProjectSettingsButton.setSelection(false);
      configureButton.setEnabled(true);
      updateFieldEditors();
    }
    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();
      }
    });
  }
}