/*******************************************************************************
 * Copyright (c) 2005, 2006 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.ide;

import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPageListener;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveRegistry;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PerspectiveAdapter;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.application.ActionBarAdvisor;
import org.eclipse.ui.application.IActionBarConfigurer;
import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
import org.eclipse.ui.application.WorkbenchWindowAdvisor;
import org.eclipse.ui.internal.ide.dialogs.WelcomeEditorInput;
import org.eclipse.ui.part.EditorInputTransfer;
import org.eclipse.ui.part.MarkerTransfer;
import org.eclipse.ui.part.ResourceTransfer;
import org.eclipse.update.configurator.ConfiguratorUtils;
import org.eclipse.update.configurator.IPlatformConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;

/**
 * Window-level advisor for the IDE.
 */
public class IDEWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {

    private static final String WELCOME_EDITOR_ID = "org.eclipse.ui.internal.ide.dialogs.WelcomeEditor"; //$NON-NLS-1$

    private IDEWorkbenchAdvisor wbAdvisor;
    private boolean editorsAndIntrosOpened = false;
    private IEditorPart lastActiveEditor = null;
    private IPerspectiveDescriptor lastPerspective = null;

    private IWorkbenchPage lastActivePage;
    private String lastEditorTitle = ""; //$NON-NLS-1$
    
    private IPropertyListener editorPropertyListener = new IPropertyListener() {
        public void propertyChanged(Object source, int propId) {
            if (propId == IWorkbenchPartConstants.PROP_TITLE) {
                if (lastActiveEditor != null) {
                    String newTitle = lastActiveEditor.getTitle();
                    if (!lastEditorTitle.equals(newTitle)) {
                        recomputeTitle();
                    }
                }
            }
        }
    };

    private IAdaptable lastInput;

    /**
     * Crates a new IDE workbench window advisor.
     *  
     * @param wbAdvisor the workbench advisor
     * @param configurer the window configurer
     */
    public IDEWorkbenchWindowAdvisor(IDEWorkbenchAdvisor wbAdvisor, IWorkbenchWindowConfigurer configurer) {
        super(configurer);
        this.wbAdvisor = wbAdvisor;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.application.WorkbenchWindowAdvisor#createActionBarAdvisor(org.eclipse.ui.application.IActionBarConfigurer)
     */
    public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) {
        return new WorkbenchActionBuilder(configurer);
    }
    
    /**
     * Returns the workbench.
     * 
     * @return the workbench
     */
    private IWorkbench getWorkbench() {
        return getWindowConfigurer().getWorkbenchConfigurer().getWorkbench();
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.application.WorkbenchAdvisor#preWindowShellClose
     */
    public boolean preWindowShellClose() {
        if (getWorkbench().getWorkbenchWindowCount() > 1) {
            return true;
        }
        // the user has asked to close the last window, while will cause the
        // workbench to close in due course - prompt the user for confirmation
        IPreferenceStore store = IDEWorkbenchPlugin.getDefault()
                .getPreferenceStore();
        boolean promptOnExit = store
                .getBoolean(IDEInternalPreferences.EXIT_PROMPT_ON_CLOSE_LAST_WINDOW);

        if (promptOnExit) {
            String message;

            String productName = null;
            IProduct product = Platform.getProduct();
            if (product != null) {
                productName = product.getName();
            }
            if (productName == null) {
                message = IDEWorkbenchMessages.PromptOnExitDialog_message0;
            } else {
                message = NLS.bind(IDEWorkbenchMessages.PromptOnExitDialog_message1, productName);
            }

            MessageDialogWithToggle dlg = MessageDialogWithToggle
                    .openOkCancelConfirm(getWindowConfigurer().getWindow()
                            .getShell(), IDEWorkbenchMessages.PromptOnExitDialog_shellTitle,
                            message, IDEWorkbenchMessages.PromptOnExitDialog_choice,
                            false, null, null);
            if (dlg.getReturnCode() != IDialogConstants.OK_ID) {
                return false;
            }
            if (dlg.getToggleState()) {
                store
                        .setValue(
                                IDEInternalPreferences.EXIT_PROMPT_ON_CLOSE_LAST_WINDOW,
                                false);
                IDEWorkbenchPlugin.getDefault().savePluginPreferences();
            }
        }

        return true;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.application.WorkbenchAdvisor#preWindowOpen
     */
    public void preWindowOpen() {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();

        // show the shortcut bar and progress indicator, which are hidden by default
        configurer.setShowPerspectiveBar(true);
        configurer.setShowFastViewBars(true);
        configurer.setShowProgressIndicator(true);

        // add the drag and drop support for the editor area
        configurer.addEditorAreaTransfer(EditorInputTransfer
                .getInstance());
        configurer.addEditorAreaTransfer(ResourceTransfer.getInstance());
        configurer.addEditorAreaTransfer(MarkerTransfer.getInstance());
        configurer
                .configureEditorAreaDropListener(new EditorAreaDropAdapter(
                        configurer.getWindow()));

        hookTitleUpdateListeners(configurer);
    }

    /**
     * Hooks the listeners needed on the window
     * @param configurer
     */
    private void hookTitleUpdateListeners(IWorkbenchWindowConfigurer configurer) {
        // hook up the listeners to update the window title
        configurer.getWindow().addPageListener(new IPageListener() {
            public void pageActivated(IWorkbenchPage page) {
                updateTitle();
            }

            public void pageClosed(IWorkbenchPage page) {
                updateTitle();
            }

            public void pageOpened(IWorkbenchPage page) {
                // do nothing
            }
        });
        configurer.getWindow().addPerspectiveListener(
                new PerspectiveAdapter() {
                    public void perspectiveActivated(IWorkbenchPage page,
                            IPerspectiveDescriptor perspective) {
                        updateTitle();
                    }
					public void perspectiveSavedAs(IWorkbenchPage page,IPerspectiveDescriptor oldPerspective,IPerspectiveDescriptor newPerspective){
						updateTitle();
					}
                    public void perspectiveDeactivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
                        updateTitle();
                    }
                });
        configurer.getWindow().getPartService().addPartListener(
                new IPartListener2() {
                    public void partActivated(IWorkbenchPartReference ref) {
                        if (ref instanceof IEditorReference) {
                            updateTitle();
                        }
                    }

                    public void partBroughtToTop(IWorkbenchPartReference ref) {
                        if (ref instanceof IEditorReference) {
                            updateTitle();
                        }
                    }

                    public void partClosed(IWorkbenchPartReference ref) {
						updateTitle();
                    }

                    public void partDeactivated(IWorkbenchPartReference ref) {
                        // do nothing
                    }

                    public void partOpened(IWorkbenchPartReference ref) {
                        // do nothing
                    }

                    public void partHidden(IWorkbenchPartReference ref) {
                        // do nothing
                    }

                    public void partVisible(IWorkbenchPartReference ref) {
                        // do nothing
                    }

                    public void partInputChanged(IWorkbenchPartReference ref) {
                        // do nothing
                    }
                });
    } 

    private String computeTitle() {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
        IWorkbenchPage currentPage = configurer.getWindow().getActivePage();
        IEditorPart activeEditor = null;
        if (currentPage != null) {
            activeEditor = currentPage.getActiveEditor();
        }
        
        String title = null;
        IProduct product = Platform.getProduct();
        if (product != null) {
            title = product.getName();
        }
        if (title == null) {
            title = ""; //$NON-NLS-1$
        }

        if (currentPage != null) {
            if (activeEditor != null) {
                lastEditorTitle = activeEditor.getTitle();
                title = NLS.bind(IDEWorkbenchMessages.WorkbenchWindow_shellTitle, lastEditorTitle, title);
            }
            IPerspectiveDescriptor persp = currentPage.getPerspective();
            String label = ""; //$NON-NLS-1$
            if (persp != null) {
				label = persp.getLabel();
			}
            IAdaptable input = currentPage.getInput();
            if (input != null && !input.equals(wbAdvisor.getDefaultPageInput())) {
                label = currentPage.getLabel();
            }
            if (label != null && !label.equals("")) { //$NON-NLS-1$ 
                title = NLS.bind(IDEWorkbenchMessages.WorkbenchWindow_shellTitle, label, title);
            }
        }

        String workspaceLocation = wbAdvisor.getWorkspaceLocation();
        if (workspaceLocation != null) {
            title = NLS.bind(IDEWorkbenchMessages.WorkbenchWindow_shellTitle, title, workspaceLocation);
        }
        
        return title;        
    }
    
    private void recomputeTitle() {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
        String oldTitle = configurer.getTitle();
        String newTitle = computeTitle();
        if (!newTitle.equals(oldTitle)) {
            configurer.setTitle(newTitle);
        }
    }
        
    /**
     * Updates the window title. Format will be:
     * [pageInput -] [currentPerspective -] [editorInput -] [workspaceLocation -] productName
     */
    private void updateTitle() {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
        IWorkbenchWindow window = configurer.getWindow();
        IEditorPart activeEditor = null;
        IWorkbenchPage currentPage = window.getActivePage();
        IPerspectiveDescriptor persp = null;
        IAdaptable input = null;
        
        if (currentPage != null) {
            activeEditor = currentPage.getActiveEditor();
            persp = currentPage.getPerspective();
            input = currentPage.getInput();
        }
        
        // Nothing to do if the editor hasn't changed
        if (activeEditor == lastActiveEditor && currentPage == lastActivePage 
                && persp == lastPerspective && input == lastInput) {
            return;
        }
        
        if (lastActiveEditor != null) {
            lastActiveEditor.removePropertyListener(editorPropertyListener );
        }
        
        lastActiveEditor = activeEditor;
        lastActivePage = currentPage;
        lastPerspective = persp;
        lastInput = input;
        
        if (activeEditor != null) {
            activeEditor.addPropertyListener(editorPropertyListener);
        }

        recomputeTitle();
    }

    
    /* (non-Javadoc)
     * @see org.eclipse.ui.application.WorkbenchAdvisor#postWindowRestore
     */
    public void postWindowRestore()
            throws WorkbenchException {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
        IWorkbenchWindow window = configurer.getWindow();
        
        int index = getWorkbench().getWorkbenchWindowCount() - 1;

        AboutInfo[] welcomePerspectiveInfos = wbAdvisor.getWelcomePerspectiveInfos(); 
        if (index >= 0 && welcomePerspectiveInfos != null
                && index < welcomePerspectiveInfos.length) {
            // find a page that exist in the window
            IWorkbenchPage page = window.getActivePage();
            if (page == null) {
                IWorkbenchPage[] pages = window.getPages();
                if (pages != null && pages.length > 0) {
					page = pages[0];
				}
            }

            // if the window does not contain a page, create one
            String perspectiveId = welcomePerspectiveInfos[index].getWelcomePerspectiveId();
            if (page == null) {
                IAdaptable root = wbAdvisor.getDefaultPageInput();
                page = window.openPage(perspectiveId,
                        root);
            } else {
                IPerspectiveRegistry reg = getWorkbench()
                        .getPerspectiveRegistry();
                IPerspectiveDescriptor desc = reg
                        .findPerspectiveWithId(perspectiveId);
                if (desc != null) {
                    page.setPerspective(desc);
                }
            }

            // set the active page and open the welcome editor
            window.setActivePage(page);
            page.openEditor(new WelcomeEditorInput(
                    welcomePerspectiveInfos[index]),
                    WELCOME_EDITOR_ID, true);
        }
    }

    /**
     * Tries to open the intro, if one exists and otherwise will open the legacy 
     * Welcome pages.
     * 
     * @see org.eclipse.ui.application.WorkbenchWindowAdvisor#openIntro()
     */
    public void openIntro() {
        if (editorsAndIntrosOpened ) {
			return;
		}

        editorsAndIntrosOpened = true;

        // don't try to open the welcome editors if there is an intro
        if (wbAdvisor.hasIntro()) {
			super.openIntro();
		} else {
            openWelcomeEditors(getWindowConfigurer().getWindow());
            // save any preferences changes caused by the above actions
            IDEWorkbenchPlugin.getDefault().savePluginPreferences();
        }
    }

    /*
     * Open the welcome editor for the primary feature and
     * for any newly installed features.
     */
    private void openWelcomeEditors(IWorkbenchWindow window) {
        if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(
                IDEInternalPreferences.WELCOME_DIALOG)) {
            // show the welcome page for the product the first time the workbench opens
            IProduct product = Platform.getProduct();
            if (product == null) {
				return;
			}

            AboutInfo productInfo = new AboutInfo(product);
            URL url = productInfo.getWelcomePageURL();
            if (url == null) {
				return;
			}

            IDEWorkbenchPlugin.getDefault().getPreferenceStore().setValue(
                    IDEInternalPreferences.WELCOME_DIALOG, false);
            openWelcomeEditor(window, new WelcomeEditorInput(productInfo), null);
        } else {
            // Show the welcome page for any newly installed features
            List welcomeFeatures = new ArrayList();
            for (Iterator it = wbAdvisor.getNewlyAddedBundleGroups().entrySet()
                    .iterator(); it.hasNext();) {
                Map.Entry entry = (Map.Entry) it.next();
                String versionedId = (String) entry.getKey();
                String featureId = versionedId.substring(0, versionedId
                        .indexOf(':'));
                AboutInfo info = (AboutInfo) entry.getValue();

                if (info != null && info.getWelcomePageURL() != null) {
                    welcomeFeatures.add(info);
                    // activate the feature plug-in so it can run some install code
                    IPlatformConfiguration platformConfiguration = ConfiguratorUtils
                            .getCurrentPlatformConfiguration();
                    IPlatformConfiguration.IFeatureEntry feature = platformConfiguration
                            .findConfiguredFeatureEntry(featureId);
                    if (feature != null) {
                        String pi = feature.getFeaturePluginIdentifier();
                        if (pi != null) {
                            //Start the bundle if there is one
                            Bundle bundle = Platform.getBundle(pi);
                            if(bundle != null) {
								try{
                                    bundle.start();
                                }
                                catch(BundleException exception){
                                    IDEWorkbenchPlugin.log("Failed to load feature", exception);//$NON-NLS-1$
                                }
                                finally {
                                	try {
										bundle.stop();
									} catch (BundleException ex) {
	                                    IDEWorkbenchPlugin.log("Failed to stop feature", ex);//$NON-NLS-1$
									}
                                }
							}
                        }
                    }
                }
            }

            int wCount = getWorkbench().getWorkbenchWindowCount();
            for (int i = 0; i < welcomeFeatures.size(); i++) {
                AboutInfo newInfo = (AboutInfo) welcomeFeatures.get(i);
                String id = newInfo.getWelcomePerspectiveId();
                // Other editors were already opened in postWindowRestore(..)
                if (id == null || i >= wCount) {
                    openWelcomeEditor(window, new WelcomeEditorInput(newInfo),
                            id);
                }
            }
        }
    }

    /**
     * Open a welcome editor for the given input
     */
    private void openWelcomeEditor(IWorkbenchWindow window,
            WelcomeEditorInput input, String perspectiveId) {
        if (getWorkbench().getWorkbenchWindowCount() == 0) {
            // Something is wrong, there should be at least
            // one workbench window open by now.
            return;
        }

        IWorkbenchWindow win = window;
        if (perspectiveId != null) {
            try {
                win = getWorkbench().openWorkbenchWindow(perspectiveId,
                        wbAdvisor.getDefaultPageInput());
                if (win == null) {
                    win = window;
                }
            } catch (WorkbenchException e) {
                IDEWorkbenchPlugin
                        .log(
                                "Error opening window with welcome perspective.", e.getStatus()); //$NON-NLS-1$
                return;
            }
        }

        if (win == null) {
            win = getWorkbench().getWorkbenchWindows()[0];
        }

        IWorkbenchPage page = win.getActivePage();
        String id = perspectiveId;
        if (id == null) {
            id = getWorkbench().getPerspectiveRegistry()
                    .getDefaultPerspective();
        }

        if (page == null) {
            try {
                page = win.openPage(id, wbAdvisor.getDefaultPageInput());
            } catch (WorkbenchException e) {
                ErrorDialog.openError(win.getShell(), IDEWorkbenchMessages.Problems_Opening_Page,
                        e.getMessage(), e.getStatus());
            }
        }
        if (page == null) {
			return;
		}

        if (page.getPerspective() == null) {
            try {
                page = getWorkbench().showPerspective(id, win);
            } catch (WorkbenchException e) {
                ErrorDialog
                        .openError(
                                win.getShell(),
                                IDEWorkbenchMessages.Workbench_openEditorErrorDialogTitle,
                                IDEWorkbenchMessages.Workbench_openEditorErrorDialogMessage,
                                e.getStatus());
                return;
            }
        }

        page.setEditorAreaVisible(true);

        // see if we already have an editor
        IEditorPart editor = page.findEditor(input);
        if (editor != null) {
            page.activate(editor);
            return;
        }

        try {
            page.openEditor(input, WELCOME_EDITOR_ID);
        } catch (PartInitException e) {
            ErrorDialog
                    .openError(
                            win.getShell(),
                            IDEWorkbenchMessages.Workbench_openEditorErrorDialogTitle,
                            IDEWorkbenchMessages.Workbench_openEditorErrorDialogMessage,
                            e.getStatus());
        }
        return;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.application.WorkbenchAdvisor#createEmptyWindowContents(org.eclipse.ui.application.IWorkbenchWindowConfigurer, org.eclipse.swt.widgets.Composite)
     */
    public Control createEmptyWindowContents(Composite parent) {
        final IWorkbenchWindow window = getWindowConfigurer().getWindow();
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new GridLayout(2, false));
        Display display = composite.getDisplay();
        Color bgCol = display.getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND);
        composite.setBackground(bgCol);
        Label label = new Label(composite, SWT.WRAP);
        label.setForeground(display.getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND));
        label.setBackground(bgCol);
        label.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
        String msg = IDEWorkbenchMessages.IDEWorkbenchAdvisor_noPerspective;
        label.setText(msg);
        ToolBarManager toolBarManager = new ToolBarManager();
        // TODO: should obtain the open perspective action from ActionFactory
        IAction openPerspectiveAction = ActionFactory.OPEN_PERSPECTIVE_DIALOG.create(window);
        toolBarManager.add(openPerspectiveAction);
        ToolBar toolBar = toolBarManager.createControl(composite);
        toolBar.setBackground(bgCol);
        return composite;
    }

}
