/*******************************************************************************
 * Copyright (c) 2006, 2015 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
 *******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.breakpoints;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IExpression;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.IDebugView;
import org.eclipse.debug.ui.InspectPopupDialog;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.debug.ui.contexts.IDebugContextManager;
import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaExceptionBreakpoint;
import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.actions.PopupInspectAction;
import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;

/**
 * This class is used to show the inspect pop-up when a thread is suspended due to an exception being thrown
 */
public class ExceptionInspector implements IDebugContextListener, IPreferenceChangeListener {

	/**
	 * Constructor
	 */
	public ExceptionInspector() {
		IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(JDIDebugUIPlugin.getUniqueIdentifier());
		if(prefs != null) {
			prefs.addPreferenceChangeListener(this);
		}
		boolean doit = Platform.getPreferencesService().getBoolean(
JDIDebugUIPlugin.getUniqueIdentifier(),
				IJDIPreferencesConstants.PREF_OPEN_INSPECT_POPUP_ON_EXCEPTION,
				false,
				null);
		if (doit) {
			DebugUITools.getDebugContextManager().addDebugContextListener(this);
		}
	}

	/**
	 * @see org.eclipse.debug.ui.contexts.IDebugContextListener#debugContextChanged(org.eclipse.debug.ui.contexts.DebugContextEvent)
	 */
	@Override
	public void debugContextChanged(DebugContextEvent event) {
		if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
			IWorkbenchPart part = event.getDebugContextProvider().getPart();
			if (part != null) {
				IWorkbenchPartSite site = part.getSite();
				if (site != null && IDebugUIConstants.ID_DEBUG_VIEW.equals(site.getId())) {
					IWorkbenchPage page = site.getWorkbenchWindow().getActivePage();
					if (page != null && page.isPartVisible(part)) {
						ISelection selection = event.getContext();
						if (selection instanceof IStructuredSelection) {
							IStructuredSelection ss = (IStructuredSelection) selection;
							if (ss.size() == 1) {
								Object firstElement = ss.getFirstElement();
								if (firstElement instanceof IAdaptable) {
									IJavaStackFrame frame = ((IAdaptable) firstElement).getAdapter(IJavaStackFrame.class);
									if (frame != null) {
										IJavaThread thread = (IJavaThread)frame.getThread();
										try {
											if (frame.equals(thread.getTopStackFrame())) {
												IBreakpoint[] breakpoints = thread.getBreakpoints();
												if (breakpoints.length == 1) {
													if (breakpoints[0] instanceof IJavaExceptionBreakpoint) {
														IJavaExceptionBreakpoint exception = (IJavaExceptionBreakpoint) breakpoints[0];
														IJavaObject lastException = ((JavaExceptionBreakpoint)exception).getLastException();
														if (lastException != null) {
															IExpression exp = new JavaInspectExpression(exception.getExceptionTypeName(), lastException);
															Tree tree = (Tree) ((IDebugView)part).getViewer().getControl();
															TreeItem[] selection2 = tree.getSelection();
															Rectangle bounds = selection2[0].getBounds();
															Point point = tree.toDisplay(bounds.x, bounds.y + bounds.height);
															InspectPopupDialog dialog = new InspectPopupDialog(part.getSite().getShell(),
																	point, PopupInspectAction.ACTION_DEFININITION_ID, exp);
															dialog.open();
														}
													}
												}
											}
										} catch (DebugException e) {}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
	 */
	@Override
	public void preferenceChange(PreferenceChangeEvent event) {
		if (IJDIPreferencesConstants.PREF_OPEN_INSPECT_POPUP_ON_EXCEPTION.equals(event.getKey())) {
			IDebugContextManager manager = DebugUITools.getDebugContextManager();
			boolean doit = Platform.getPreferencesService().getBoolean(
JDIDebugUIPlugin.getUniqueIdentifier(),
					IJDIPreferencesConstants.PREF_OPEN_INSPECT_POPUP_ON_EXCEPTION,
					false,
					null);
			if (doit) {
				manager.addDebugContextListener(this);
 			} else {
 				manager.removeDebugContextListener(this);
 			}
		}
	}
}