/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Patrik Suzzi <psuzzi@gmail.com> - Bug 489250
 *******************************************************************************/

package org.eclipse.ui.internal.ide;

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

import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IBundleGroup;
import org.eclipse.core.runtime.IBundleGroupProvider;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.ide.registry.MarkerImageProviderRegistry;
import org.eclipse.ui.internal.ide.registry.ProjectImageRegistry;
import org.eclipse.ui.internal.ide.registry.UnassociatedEditorStrategyRegistry;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

import com.ibm.icu.text.MessageFormat;

/**
 * This internal class represents the top of the IDE workbench.
 *
 * This class is responsible for tracking various registries
 * font, preference, graphics, dialog store.
 *
 * This class is explicitly referenced by the
 * IDE workbench plug-in's  "plugin.xml"
 *
 * @since 3.0
 */
public class IDEWorkbenchPlugin extends AbstractUIPlugin {
    // Default instance of the receiver
    private static IDEWorkbenchPlugin inst;

    /**
     * The IDE workbench plugin ID.
     */
    public static final String IDE_WORKBENCH = "org.eclipse.ui.ide"; //$NON-NLS-1$

    /**
     * The ID of the default text editor.
     * This must correspond to EditorsUI.DEFAULT_TEXT_EDITOR_ID.
     */
    public static final String DEFAULT_TEXT_EDITOR_ID = "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$

    // IDE workbench extension point names
    public static final String PL_MARKER_IMAGE_PROVIDER = "markerImageProviders"; //$NON-NLS-1$

    public static final String PL_MARKER_HELP = "markerHelp"; //$NON-NLS-1$

    public static final String PL_MARKER_RESOLUTION = "markerResolution"; //$NON-NLS-1$

    public static final String PL_PROJECT_NATURE_IMAGES = "projectNatureImages"; //$NON-NLS-1$

	private static final String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$

	private static final int PROBLEMS_VIEW_CREATION_DELAY= 6000;

    /**
     * Project image registry; lazily initialized.
     */
    private ProjectImageRegistry projectImageRegistry = null;

    /**
     * Marker image registry; lazily initialized.
     */
    private MarkerImageProviderRegistry markerImageProviderRegistry = null;

	/**
	 * Unassociated file/editor strategy registry; lazily initialized
	 */
	private UnassociatedEditorStrategyRegistry unassociatedEditorStrategyRegistry = null;

	private ResourceManager resourceManager;

	/**
	 * Create an instance of the receiver.
	 */
	public IDEWorkbenchPlugin() {
		super();
		inst = this;
	}
    /**
     * Creates an extension.  If the extension plugin has not
     * been loaded a busy cursor will be activated during the duration of
     * the load.
     *
     * @param element the config element defining the extension
     * @param classAttribute the name of the attribute carrying the class
     * @return Object the extension object
     * @throws CoreException
     */
    public static Object createExtension(final IConfigurationElement element,
			final String classAttribute) throws CoreException {
		// If plugin has been loaded create extension.
		// Otherwise, show busy cursor then create extension.
		Bundle plugin = Platform.getBundle(element.getContributor().getName());
		if (plugin.getState() == Bundle.ACTIVE) {
			return element.createExecutableExtension(classAttribute);
		}
		final Object[] ret = new Object[1];
		final CoreException[] exc = new CoreException[1];
		BusyIndicator.showWhile(null, () -> {
			try {
				ret[0] = element.createExecutableExtension(classAttribute);
			} catch (CoreException e) {
				exc[0] = e;
			}
		});
		if (exc[0] != null) {
			throw exc[0];
		}
		return ret[0];
    }

    /* Return the default instance of the receiver. This represents the runtime plugin.
     *
     * @see AbstractPlugin for the typical implementation pattern for plugin classes.
     */
    public static IDEWorkbenchPlugin getDefault() {
        return inst;
    }

    /**
     * Return the workspace used by the workbench
     *
     * This method is internal to the workbench and must not be called
     * by any plugins.
     */
    public static IWorkspace getPluginWorkspace() {
        return ResourcesPlugin.getWorkspace();
    }

    /**
     * Logs the given message to the platform log.
     *
     * If you have an exception in hand, call log(String, Throwable) instead.
     *
     * If you have a status object in hand call log(String, IStatus) instead.
     *
     * This convenience method is for internal use by the IDE Workbench only and
     * must not be called outside the IDE Workbench.
     *
     * @param message
     *            A high level UI message describing when the problem happened.
     */
    public static void log(String message) {
        getDefault().getLog().log(
                StatusUtil.newStatus(IStatus.ERROR, message, null));
    }

    /**
     * Logs the given message and throwable to the platform log.
     *
     * If you have a status object in hand call log(String, IStatus) instead.
     *
     * This convenience method is for internal use by the IDE Workbench only and
     * must not be called outside the IDE Workbench.
     *
     * @param message
     *            A high level UI message describing when the problem happened.
     * @param t
     *            The throwable from where the problem actually occurred.
     */
    public static void log(String message, Throwable t) {
        IStatus status = StatusUtil.newStatus(IStatus.ERROR, message, t);
        log(message, status);
    }

    /**
     * Logs the given throwable to the platform log, indicating the class and
     * method from where it is being logged (this is not necessarily where it
     * occurred).
     *
     * This convenience method is for internal use by the IDE Workbench only and
     * must not be called outside the IDE Workbench.
     *
     * @param clazz
     *            The calling class.
     * @param methodName
     *            The calling method name.
     * @param t
     *            The throwable from where the problem actually occurred.
     */
	public static void log(Class<?> clazz, String methodName, Throwable t) {
        String msg = MessageFormat.format("Exception in {0}.{1}: {2}", //$NON-NLS-1$
				clazz.getName(), methodName, t);
        log(msg, t);
    }

    /**
     * Logs the given message and status to the platform log.
     *
     * This convenience method is for internal use by the IDE Workbench only and
     * must not be called outside the IDE Workbench.
     *
     * @param message
     *            A high level UI message describing when the problem happened.
     *            May be <code>null</code>.
     * @param status
     *            The status describing the problem. Must not be null.
     */
    public static void log(String message, IStatus status) {

        //1FTUHE0: ITPCORE:ALL - API - Status & logging - loss of semantic info

        if (message != null) {
            getDefault().getLog().log(
                    StatusUtil.newStatus(IStatus.ERROR, message, null));
        }

        getDefault().getLog().log(status);
    }

    @Override
	protected void refreshPluginActions() {
        // do nothing
    }


    /**
     * Return the manager that maps project nature ids to images.
     */
    public ProjectImageRegistry getProjectImageRegistry() {
        if (projectImageRegistry == null) {
            projectImageRegistry = new ProjectImageRegistry();
            projectImageRegistry.load();
        }
        return projectImageRegistry;
    }

    /**
     * Returns the marker image provider registry for the workbench.
     *
     * @return the marker image provider registry
     */
    public MarkerImageProviderRegistry getMarkerImageProviderRegistry() {
        if (markerImageProviderRegistry == null) {
            markerImageProviderRegistry = new MarkerImageProviderRegistry();
        }
        return markerImageProviderRegistry;
    }

	/**
	 * Returns the unassociated file/editor strategy registry for the workbench.
	 *
	 * @return the unassociated file/editor strategy registry
	 */
	public synchronized UnassociatedEditorStrategyRegistry getUnassociatedEditorStrategyRegistry() {
		if (unassociatedEditorStrategyRegistry == null) {
			unassociatedEditorStrategyRegistry = new UnassociatedEditorStrategyRegistry();
		}
		return unassociatedEditorStrategyRegistry;
	}

    /**
     * Returns the about information of all known features,
     * omitting any features which are missing this information.
     *
     * @return a possibly empty list of about infos
     */
    public AboutInfo[] getFeatureInfos() {
        // cannot be cached since bundle groups come and go
		List<AboutInfo> infos = new ArrayList<>();

        // add an entry for each bundle group
        IBundleGroupProvider[] providers = Platform.getBundleGroupProviders();
        if (providers != null) {
			for (IBundleGroupProvider provider : providers) {
				for (IBundleGroup bundleGroup : provider.getBundleGroups()) {
					infos.add(new AboutInfo(bundleGroup));
				}
            }
		}

        return infos.toArray(new AboutInfo[infos.size()]);
    }
	/**
	 * Get the workbench image with the given path relative to
	 * ICON_PATH.
	 * @param relativePath
	 * @return ImageDescriptor
	 */
	public static ImageDescriptor getIDEImageDescriptor(String relativePath){
		return imageDescriptorFromPlugin(IDE_WORKBENCH, ICONS_PATH + relativePath);
	}
	/**
	 * Return the resourceManager used by this plug-in.
	 * @return
	 */
	public ResourceManager getResourceManager() {
		if(resourceManager == null){
			resourceManager = new LocalResourceManager(JFaceResources.getResources());
		}
		return resourceManager;
	}

	@Override
	public void stop(BundleContext context) throws Exception {
		super.stop(context);
		if (resourceManager != null)
			resourceManager.dispose();
	}

	@Override
	public void start(BundleContext context) throws Exception {
		super.start(context);

		createProblemsViews();
	}

	/**
	 * Create (but don't activate) the Problems views so that the view's tooltip and icon are
	 * up-to-date.
	 */
	private void createProblemsViews() {
		final Runnable r= new Runnable() {
			@Override
			public void run() {
				IWorkbench workbench = PlatformUI.isWorkbenchRunning() ? PlatformUI.getWorkbench() : null;
				if (workbench != null && (workbench.getDisplay().isDisposed() || workbench.isClosing()))
					return;

				if (workbench == null || workbench.isStarting()) {
					Display.getCurrent().timerExec(PROBLEMS_VIEW_CREATION_DELAY, this);
					return;
				}
				// We can't access preferences store before scheduling the job
				// because this would cause instance area to be initialized
				// before user selected the workspace location.
				// See bug 514297 and
				// org.eclipse.core.internal.runtime.DataArea.assertLocationInitialized()
				if (!getDefault().getPreferenceStore()
						.getBoolean(IDEInternalPreferences.SHOW_PROBLEMS_VIEW_DECORATIONS_ON_STARTUP)) {
					return;
				}
				for (IWorkbenchWindow window : workbench.getWorkbenchWindows()) {
					IWorkbenchPage activePage= window.getActivePage();
					if (activePage == null)
						continue;
					for (IViewReference viewReference : activePage.getViewReferences()) {
						if (IPageLayout.ID_PROBLEM_VIEW.equals(viewReference.getId()))
							try {
								activePage.showView(viewReference.getId(), viewReference.getSecondaryId(), IWorkbenchPage.VIEW_CREATE);
							} catch (PartInitException e) {
								log("Could not create Problems view", e.getStatus()); //$NON-NLS-1$
							}
					}
				}
			}
		};
		Display display = Display.getCurrent();
		if (display != null) {
			display.timerExec(PROBLEMS_VIEW_CREATION_DELAY, r);
		} else {
			Job job = new Job("Initializing Problems view") { //$NON-NLS-1$
				@Override
				protected IStatus run(IProgressMonitor monitor) {
					IWorkbench workbench = PlatformUI.isWorkbenchRunning() ? PlatformUI.getWorkbench() : null;
					if (workbench == null) {
						// Workbench not created yet, so avoid using display to
						// avoid crash like in bug 513901
						schedule(PROBLEMS_VIEW_CREATION_DELAY);
						return Status.OK_STATUS;
					}
					if (workbench.isClosing()) {
						return Status.CANCEL_STATUS;
					}
					PlatformUI.getWorkbench().getDisplay().asyncExec(r);
					return Status.OK_STATUS;
				}
			};
			job.setSystem(true);
			job.setUser(false);
			job.schedule(PROBLEMS_VIEW_CREATION_DELAY);
		}
	}
}
