/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal.dialogs;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IPreferenceConstants;
import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPage;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.registry.PerspectiveDescriptor;
import org.eclipse.ui.internal.registry.PerspectiveRegistry;
import org.eclipse.ui.internal.util.Descriptors;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.internal.util.Util;

/**
 * The Workbench / Perspectives preference page.
 */
public class PerspectivesPreferencePage extends PreferencePage implements
		IWorkbenchPreferencePage {
	private IWorkbench workbench;

	private PerspectiveRegistry perspectiveRegistry;

	private ArrayList perspectives;

	private String defaultPerspectiveId;

	private ArrayList perspToDelete = new ArrayList();

	private ArrayList perspToRevert = new ArrayList();

	private Table perspectivesTable;
	
	private Button revertButton;

	private Button deleteButton;

	private Button setDefaultButton;

	// widgets for open perspective mode;
	private Button openSameWindowButton;

	private Button openNewWindowButton;

	private int openPerspMode;

	// widgets for open view mode
	private int openViewMode;

	private Button openEmbedButton;

	private Button openFastButton;
    
	// labels
	private final String OVM_TITLE = WorkbenchMessages.OpenViewMode_title;

	private final String OVM_EMBED = WorkbenchMessages.OpenViewMode_embed;

	private final String OVM_FAST = WorkbenchMessages.OpenViewMode_fast; 

	private final String OPM_TITLE = WorkbenchMessages.OpenPerspectiveMode_optionsTitle; 

	private final String OPM_SAME_WINDOW = WorkbenchMessages.OpenPerspectiveMode_sameWindow; 

	private final String OPM_NEW_WINDOW = WorkbenchMessages.OpenPerspectiveMode_newWindow; 

	/**
	 * <code>Comparator</code> to compare two perspective descriptors
	 */
    private Comparator comparator = new Comparator() {
        private Collator collator = Collator.getInstance();

        public int compare(Object ob1, Object ob2) {
            IPerspectiveDescriptor d1 = (IPerspectiveDescriptor) ob1;
            IPerspectiveDescriptor d2 = (IPerspectiveDescriptor) ob2;
            return collator.compare(d1.getLabel(), d2.getLabel());
        }
    };

	/**
	 * Creates the page's UI content.
	 */
	protected Control createContents(Composite parent) {
		// @issue if the product subclasses this page, then it should provide
		// the help content
		PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
				IWorkbenchHelpContextIds.PERSPECTIVES_PREFERENCE_PAGE);

		Composite composite = createComposite(parent);

		createOpenPerspButtonGroup(composite);
		createOpenViewButtonGroup(composite);
		createCustomizePerspective(composite);

		return composite;
	}

	/**
	 * Creates the composite which will contain all the preference controls for
	 * this page.
	 * 
	 * @param parent
	 *            the parent composite
	 * @return the composite for this page
	 */
	protected Composite createComposite(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridData data = new GridData(GridData.FILL_BOTH);
		composite.setLayoutData(data);
		composite.setFont(parent.getFont());
		GridLayout layout = new GridLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.verticalSpacing = 10;
		composite.setLayout(layout);
		return composite;
	}

	/**
	 * Create a composite that contains buttons for selecting the open
	 * perspective mode.
	 * 
	 * @param composite
	 *            the parent composite
	 */
	protected void createOpenPerspButtonGroup(Composite composite) {

		Font font = composite.getFont();

		Group buttonComposite = new Group(composite, SWT.LEFT);
		buttonComposite.setText(OPM_TITLE);
		buttonComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		buttonComposite.setFont(composite.getFont());
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		buttonComposite.setLayout(layout);

		openSameWindowButton = new Button(buttonComposite, SWT.RADIO);
		openSameWindowButton.setText(OPM_SAME_WINDOW);
		openSameWindowButton
				.setSelection(IPreferenceConstants.OPM_ACTIVE_PAGE == openPerspMode);
		openSameWindowButton.setFont(font);
		openSameWindowButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				openPerspMode = IPreferenceConstants.OPM_ACTIVE_PAGE;
			}
		});

		openNewWindowButton = new Button(buttonComposite, SWT.RADIO);
		openNewWindowButton.setText(OPM_NEW_WINDOW);
		openNewWindowButton
				.setSelection(IPreferenceConstants.OPM_NEW_WINDOW == openPerspMode);
		openNewWindowButton.setFont(font);
		openNewWindowButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				openPerspMode = IPreferenceConstants.OPM_NEW_WINDOW;
			}
		});

	}

	/**
	 * Creates a composite that contains buttons for selecting open view mode.
	 * 
	 * @param composite
	 *            the parent composite
	 */
	protected void createOpenViewButtonGroup(Composite composite) {

		Font font = composite.getFont();

		Group buttonComposite = new Group(composite, SWT.LEFT);
		buttonComposite.setText(OVM_TITLE);
		buttonComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		buttonComposite.setFont(composite.getFont());
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		buttonComposite.setLayout(layout);

		openEmbedButton = new Button(buttonComposite, SWT.RADIO);
		openEmbedButton.setText(OVM_EMBED);
		openEmbedButton
				.setSelection(openViewMode == IPreferenceConstants.OVM_EMBED);
		openEmbedButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				openViewMode = IPreferenceConstants.OVM_EMBED;
			}
		});
		openEmbedButton.setFont(font);

		// Open view as float no longer supported
		if (openViewMode == IPreferenceConstants.OVM_FLOAT)
			openViewMode = IPreferenceConstants.OVM_FAST;

		openFastButton = new Button(buttonComposite, SWT.RADIO);
		openFastButton.setText(OVM_FAST);
		openFastButton
				.setSelection(openViewMode == IPreferenceConstants.OVM_FAST);
		openFastButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				openViewMode = IPreferenceConstants.OVM_FAST;
			}
		});
		openFastButton.setFont(font);

	}

	/**
	 * Create a table of 3 buttons to enable the user to manage customized
	 * perspectives.
	 * 
	 * @param parent
	 *            the parent for the button parent
	 * @return Composite that the buttons are created in.
	 */
	protected Composite createCustomizePerspective(Composite parent) {

		Font font = parent.getFont();

		// define container & its gridding
		Composite perspectivesComponent = new Composite(parent, SWT.NONE);
		perspectivesComponent.setLayoutData(new GridData(GridData.FILL_BOTH));
		perspectivesComponent.setFont(parent.getFont());

		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		perspectivesComponent.setLayout(layout);

		// Add the label
		Label label = new Label(perspectivesComponent, SWT.LEFT);
		label.setText(WorkbenchMessages.PerspectivesPreference_available); 
		GridData data = new GridData();
		data.horizontalSpan = 2;
		label.setLayoutData(data);
		label.setFont(font);

		// Add perspectivesTable.
		perspectivesTable = new Table(perspectivesComponent, SWT.H_SCROLL | SWT.V_SCROLL
				| SWT.BORDER);
	    perspectivesTable.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				updateButtons();
			}
		});
        perspectivesTable.setFont(font);

		data = new GridData(GridData.FILL_BOTH);
		data.grabExcessHorizontalSpace = true;
		data.grabExcessVerticalSpace = true;
		perspectivesTable.setLayoutData(data);

		// Populate the perspectivesTable
		IPerspectiveDescriptor[] persps = perspectiveRegistry.getPerspectives();
		perspectives = new ArrayList(persps.length);
		for (int i = 0; i < persps.length; i++)
			perspectives.add(i, persps[i]);
		Collections.sort(perspectives, comparator);
		defaultPerspectiveId = perspectiveRegistry.getDefaultPerspective();
		updatePerspectivesTable();
		
		// Create vertical button bar.
		Composite buttonBar = (Composite) createVerticalButtonBar(perspectivesComponent);
		data = new GridData(GridData.FILL_VERTICAL);
		buttonBar.setLayoutData(data);
		
		//Add note label
		String NOTE_LABEL = WorkbenchMessages.Preference_note;
		String REVERT_NOTE = WorkbenchMessages.RevertPerspective_note; 
		Composite noteComposite = createNoteComposite(font, parent,
                NOTE_LABEL, REVERT_NOTE);
        GridData noteData = new GridData();
        noteData.horizontalSpan = 2;
        noteComposite.setLayoutData(noteData);
		return perspectivesComponent;
	}

	/**
	 * Creates a new vertical button with the given id.
	 * <p>
	 * The default implementation of this framework method creates a standard
	 * push button, registers for selection events including button presses and
	 * help requests, and registers default buttons with its shell. The button
	 * id is stored as the buttons client data.
	 * </p>
	 * 
	 * @param parent
	 *            the parent composite
	 * @param label
	 *            the label from the button
	 * @param defaultButton
	 *            <code>true</code> if the button is to be the default button,
	 *            and <code>false</code> otherwise
	 * @return Button The created button.
	 */
	protected Button createVerticalButton(Composite parent, String label,
			boolean defaultButton) {
		Button button = new Button(parent, SWT.PUSH);

		button.setText(label);

		GridData data = setButtonLayoutData(button);
		data.horizontalAlignment = GridData.FILL;

		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				verticalButtonPressed(event.widget);
			}
		});
		button.setToolTipText(label);
		if (defaultButton) {
			Shell shell = parent.getShell();
			if (shell != null) {
				shell.setDefaultButton(button);
			}
		}
		button.setFont(parent.getFont());
		return button;
	}

	/**
	 * Creates and returns the vertical button bar.
	 * 
	 * @param parent
	 *            the parent composite to contain the button bar
	 * @return the button bar control
	 */
	protected Control createVerticalButtonBar(Composite parent) {
		// Create composite.
		Composite composite = new Composite(parent, SWT.NULL);

		// create a layout with spacing and margins appropriate for the font
		// size.
		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		layout.marginWidth = 5;
		layout.marginHeight = 0;
		layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
		layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
		composite.setLayout(layout);
		composite.setFont(parent.getFont());

		// Add the buttons to the button bar.
		setDefaultButton = createVerticalButton(composite, WorkbenchMessages.PerspectivesPreference_MakeDefault, false); 
		setDefaultButton.setToolTipText(WorkbenchMessages.PerspectivesPreference_MakeDefaultTip); 

		revertButton = createVerticalButton(composite, WorkbenchMessages.PerspectivesPreference_Reset, false); 
		revertButton.setToolTipText(WorkbenchMessages.PerspectivesPreference_ResetTip); 

		deleteButton = createVerticalButton(composite, WorkbenchMessages.PerspectivesPreference_Delete, false); 
		deleteButton.setToolTipText(WorkbenchMessages.PerspectivesPreference_DeleteTip); 
		updateButtons();

		return composite;
	}

	/**
	 * @see IWorkbenchPreferencePage
	 */
	public void init(IWorkbench aWorkbench) {
		this.workbench = aWorkbench;
		this.perspectiveRegistry = (PerspectiveRegistry) workbench
				.getPerspectiveRegistry();
		IPreferenceStore store = WorkbenchPlugin.getDefault()
				.getPreferenceStore();
		setPreferenceStore(store);

		openViewMode = store.getInt(IPreferenceConstants.OPEN_VIEW_MODE);
		openPerspMode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
	}

	/**
	 * The default button has been pressed.
	 */
	protected void performDefaults() {
		//Project perspective preferences
		IPreferenceStore store = WorkbenchPlugin.getDefault()
				.getPreferenceStore();

		openViewMode = store.getDefaultInt(IPreferenceConstants.OPEN_VIEW_MODE);
		// Open view as float no longer supported
		if (openViewMode == IPreferenceConstants.OVM_FLOAT)
			openViewMode = IPreferenceConstants.OVM_FAST;
		openEmbedButton
				.setSelection(openViewMode == IPreferenceConstants.OVM_EMBED);
		openFastButton
				.setSelection(openViewMode == IPreferenceConstants.OVM_FAST);

		openPerspMode = store
				.getDefaultInt(IPreferenceConstants.OPEN_PERSP_MODE);
		openSameWindowButton
				.setSelection(IPreferenceConstants.OPM_ACTIVE_PAGE == openPerspMode);
		openNewWindowButton
				.setSelection(IPreferenceConstants.OPM_NEW_WINDOW == openPerspMode);

		String currentDefault = perspectiveRegistry.getDefaultPerspective();
		
		int index = indexOf(currentDefault);
		if (index >= 0){
			defaultPerspectiveId = currentDefault;
			updatePerspectivesTable();
			perspectivesTable.setSelection(index);			
		}
		
		String newDefault = PrefUtil.getAPIPreferenceStore().getDefaultString(
                IWorkbenchPreferenceConstants.DEFAULT_PERSPECTIVE_ID);
		
		IPerspectiveDescriptor desc = null;
        if (newDefault != null)
           desc = workbench.getPerspectiveRegistry().findPerspectiveWithId(newDefault);
        if (desc == null) {
        	newDefault = workbench.getPerspectiveRegistry().getDefaultPerspective();
        }
        
        defaultPerspectiveId = newDefault;
        updatePerspectivesTable();

	}

	/**
	 * Look up the index of the perpective with the given if.
	 * @param perspectiveId or <code>null</code>
	 * @return int -1 if it cannot be found
	 */
	private int indexOf(String perspectiveId) {
		if (perspectiveId == null)
			return -1;
		PerspectiveDescriptor[] descriptors =
			new PerspectiveDescriptor[perspectives.size()];
		perspectives.toArray(descriptors);
		for (int i = 0; i < descriptors.length; i++) {
			PerspectiveDescriptor descriptor = descriptors[i];
			if(descriptor.getId().equals(perspectiveId))
				return i;
		}
		return -1;
	}

	/**
	 * Deletes the perspectives selected by the user if there is no opened
	 * instance of that perspective.
	 * 
	 * @return boolean <code>true</code> if all of the perspectives could be
	 *         deleted.
	 */
	private boolean findOpenInstance(IPerspectiveDescriptor desc) {
		IWorkbenchWindow windows[] = workbench.getWorkbenchWindows();
		
		//find all active perspectives currently
		for (int i = 0; i < windows.length; i++) {
			IWorkbenchPage pages[] = windows[i].getPages();
			for (int j = 0; j < pages.length; j++) {
				WorkbenchPage page = (WorkbenchPage) pages[j];
				if (page.findPerspective(desc) != null) {
						MessageDialog
								.openInformation(
										getShell(),
										WorkbenchMessages.PerspectivesPreference_cannotdelete_title,
										NLS.bind(WorkbenchMessages.PerspectivesPreference_cannotdelete_message,desc.getLabel()));
						return true;
				}
			}
		}

		return false;
	}

	/**
	 * Apply the user's changes if any
	 */
	public boolean performOk() {
		// Set the default perspective
		if (!Util.equals(defaultPerspectiveId, perspectiveRegistry.getDefaultPerspective())) {
			perspectiveRegistry.setDefaultPerspective(defaultPerspectiveId);
		}
		
		//Delete the perspective
		if(perspectives.size()<perspectiveRegistry.getPerspectives().length)
        	perspectiveRegistry.deletePerspectives(perspToDelete);
				
        // Revert the perspectives
		perspectiveRegistry.revertPerspectives(perspToRevert);

		IPreferenceStore store = getPreferenceStore();

		// store the open view mode setting
		store.setValue(IPreferenceConstants.OPEN_VIEW_MODE, openViewMode);

		// store the open perspective mode setting
		store.setValue(IPreferenceConstants.OPEN_PERSP_MODE, openPerspMode);

		// save both the API prefs and the internal prefs
		// the API prefs are modified by
		// PerspectiveRegistry.setDefaultPerspective
		PrefUtil.savePrefs();

		return true;
	}

	/**
	 * Update the button enablement state.
	 */
	protected void updateButtons() {
		// Get selection.
		int index = perspectivesTable.getSelectionIndex();

		// Map it to the perspective descriptor
		PerspectiveDescriptor desc = null;
		if (index > -1)
			desc = (PerspectiveDescriptor) perspectives.get(index);

		// Do enable.
		if (desc != null) {
			revertButton.setEnabled(desc.isPredefined()
					&& desc.hasCustomDefinition()
					&& !perspToRevert.contains(desc));
			deleteButton.setEnabled(!desc.isPredefined());
			setDefaultButton.setEnabled(true);
		} else {
			revertButton.setEnabled(false);
			deleteButton.setEnabled(false);
			setDefaultButton.setEnabled(false);
		}
	}

	/**
	 * Update the perspectivesTable.
	 */
	protected void updatePerspectivesTable() {
        // Populate the table with the items
		perspectivesTable.removeAll();
		for (int i = 0; i < perspectives.size(); i++) {
        	PerspectiveDescriptor persp = (PerspectiveDescriptor) perspectives.get(i);
        	newPerspectivesTableItem(persp, i, false);
        }
    }
	
	/**
	 * Create a new tableItem using given perspective, and set image for the new item.
	 */
	protected TableItem newPerspectivesTableItem(IPerspectiveDescriptor persp,
            int index, boolean selected) {
        
        ImageDescriptor image = persp.getImageDescriptor();
        
        TableItem item = new TableItem(perspectivesTable, SWT.NULL, index);
        if (image != null) {
            Descriptors.setImage(item, image);
        }
        String label=persp.getLabel();
        if (persp.getId().equals(defaultPerspectiveId)){
			label = NLS.bind(WorkbenchMessages.PerspectivesPreference_defaultLabel, label ); 
    	    
		}
        item.setText(label);
        item.setData(persp);
        if (selected) {
        	perspectivesTable.setSelection(index);
        }

        return item;
    }

    /**
	 * Notifies that this page's button with the given id has been pressed.
	 * 
	 * @param button
	 *            the button that was pressed
	 */
	protected void verticalButtonPressed(Widget button) {
		// Get selection.
		int index = perspectivesTable.getSelectionIndex();

		// Map it to the perspective descriptor
		PerspectiveDescriptor desc = null;
		if (index > -1)
			desc = (PerspectiveDescriptor) perspectives.get(index);
		else
			return;

		// Take action.
		if (button == revertButton) {
			if (desc.isPredefined() && !perspToRevert.contains(desc)) {
				perspToRevert.add(desc);
			}
		} else if (button == deleteButton) {
			if (!desc.isPredefined() && !perspToDelete.contains(desc)) {
				if(!findOpenInstance(desc)){
					perspToDelete.add(desc);
					perspToRevert.remove(desc);
					perspectives.remove(desc);				
					updatePerspectivesTable();
				}
					
			}
		} else if (button == setDefaultButton) {
			defaultPerspectiveId = desc.getId();
			updatePerspectivesTable();
			perspectivesTable.setSelection(index);
		}

		updateButtons();
	}
    
    public void dispose() {
         super.dispose();
    }
}
