/*******************************************************************************
 *  Copyright (c) 2006, 2016 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
 *     Wind River Systems, Inc. - extended implementation
 *     Navid Mehregani (TI) - Bugzilla 310191 - Detail pane does not clear up when DSF-GDB session is terminated
 *******************************************************************************/
package org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.numberformat.detail;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;

import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.debug.internal.ui.IDsfDebugHelpContextIds;
import org.eclipse.cdt.dsf.debug.internal.ui.IInternalDsfDebugUIConstants;
import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.DetailPaneMaxLengthAction;
import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.DetailPaneWordWrapAction;
import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.MessagesForDetailPane;
import org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport.TextViewerAction;
import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueVMUtil;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.ui.concurrent.SimpleDisplayExecutor;
import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode;
import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.VMPropertiesUpdate;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IDebugModelProvider;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.IDebugView;
import org.eclipse.debug.ui.IDetailPane2;
import org.eclipse.debug.ui.IValueDetailListener;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.texteditor.IUpdate;

public class NumberFormatDetailPane implements IDetailPane2, IAdaptable, IPropertyChangeListener {

	/**
	 * The <code>IWorkbenchPartSite</code> that the details area (and the
	 * variables view) belongs to.
	 */
	private IWorkbenchPartSite fWorkbenchPartSite;

	/**
	 * Map of actions. Keys are strings, values
	 * are <code>IAction</code>.
	 */
	private Map<String, IAction> fActionMap = new HashMap<>();

	/**
	 * Collection to track actions that should be updated when selection occurs.
	 */
	private List<String> fSelectionActions = new ArrayList<>();

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#init(org.eclipse.ui.IWorkbenchPartSite)
	 */
	@Override
	public void init(IWorkbenchPartSite workbench) {
		fWorkbenchPartSite = workbench;

	}

	/**
	 * Adds an action to the Map storing actions.  Removes it if action is null.
	 *
	 * @param actionID The ID of the action, used as the key in the Map
	 * @param action The action associated with the ID
	 */
	protected void setAction(String actionID, IAction action) {
		if (action == null) {
			fActionMap.remove(actionID);
		} else {
			fActionMap.put(actionID, action);
		}
	}

	/**
	 * Adds the given action to the global action handler for the ViewSite.
	 * A call to <code>updateActionBars()</code> must be called after changes
	 * to propagate changes through the workbench.
	 *
	 * @param actionID The ID of the action
	 * @param action The action to be set globally
	 */
	protected void setGlobalAction(String actionID, IAction action) {
		getViewSite().getActionBars().setGlobalActionHandler(actionID, action);
	}

	/**
	 * Adds the given action to the list of actions that will be updated when
	 * <code>updateSelectionDependentActions()</code> is called.  If the string
	 * is null it will not be added to the list.
	 *
	 * @param actionID The ID of the action which should be updated
	 */
	protected void setSelectionDependantAction(String actionID) {
		if (actionID != null)
			fSelectionActions.add(actionID);
	}

	/**
	 * Gets the action out of the map, casts it to an <code>IAction</code>
	 *
	 * @param actionID  The ID of the action to find
	 * @return The action associated with the ID or null if none is found.
	 */
	protected IAction getAction(String actionID) {
		return fActionMap.get(actionID);
	}

	/**
	 * Calls the update method of the action with the given action ID.
	 * The action must exist in the action map and must be an instance of
	 * </code>IUpdate</code>
	 *
	 * @param actionId The ID of the action to update
	 */
	protected void updateAction(String actionId) {
		IAction action = getAction(actionId);
		if (action instanceof IUpdate) {
			((IUpdate) action).update();
		}
	}

	/**
	 * Iterates through the list of selection dependent actions and
	 * updates them.  Use <code>setSelectionDependentAction(String actionID)</code>
	 * to add an action to the list.  The action must have been added to the known
	 * actions map by calling <code>setAction(String actionID, IAction action)</code>
	 * before it can be updated by this method.
	 */
	protected void updateSelectionDependentActions() {
		Iterator<String> iterator = fSelectionActions.iterator();
		while (iterator.hasNext()) {
			updateAction(iterator.next());
		}
	}

	/**
	 * Gets the view site for this view.  May be null if this detail pane
	 * is not part of a view.
	 *
	 * @return The site for this view or <code>null</code>
	 */
	protected IViewSite getViewSite() {
		if (fWorkbenchPartSite == null) {
			return null;
		} else {
			return (IViewSite) fWorkbenchPartSite.getPart().getSite();
		}
	}

	/**
	 * Gets the workbench part site for this view.  May be null if this detail pane
	 * is not part of a view.
	 *
	 * @return The workbench part site or <code>null</code>
	 */
	protected IWorkbenchPartSite getWorkbenchPartSite() {
		return fWorkbenchPartSite;
	}

	/**
	 * Returns whether this detail pane is being displayed in a view with a workbench part site.
	 *
	 * @return whether this detail pane is being displayed in a view with a workbench part site.
	 */
	protected boolean isInView() {
		return fWorkbenchPartSite != null;
	}

	/**
	 * These are the IDs for the actions in the context menu
	 */
	protected static final String DETAIL_COPY_ACTION = ActionFactory.COPY.getId() + ".TextDetailPane"; //$NON-NLS-1$
	protected static final String DETAIL_SELECT_ALL_ACTION = IDebugView.SELECT_ALL_ACTION + ".TextDetailPane"; //$NON-NLS-1$
	protected static final String DETAIL_WORD_WRAP_ACTION = DsfUIPlugin.PLUGIN_ID + ".detail_pane_word_wrap"; //$NON-NLS-1$
	protected static final String DETAIL_MAX_LENGTH_ACTION = "MaxLength"; //$NON-NLS-1$

	/**
	 * The ID, name and description of this pane are stored in constants so that the class
	 * does not have to be instantiated to access them.
	 */
	public static final String ID = "NumberFormatPane"; //$NON-NLS-1$

	/**
	 * Useful shortened names for the internationalized strings.
	 */
	public static String FORMAT_SEPARATOR = MessagesForNumberFormatDetailPane.NumberFormatDetailPane_format_separator__label;
	public static String NAME = MessagesForNumberFormatDetailPane.NumberFormatDetailPane_Name_label;
	public static String SPACES = MessagesForNumberFormatDetailPane.NumberFormatDetailPane_Spaces_label;
	public static String CRLF = MessagesForNumberFormatDetailPane.NumberFormatDetailPane_CarriageReturn_label;
	public static String DOTS = MessagesForNumberFormatDetailPane.NumberFormatDetailPane_DotDotDot_label;
	public static String PARENTHESES = MessagesForNumberFormatDetailPane.NumberFormatDetailPane__End_parentheses;

	/**
	 * Job to compute the details for a selection
	 */
	class DetailJob extends Job implements IValueDetailListener {
		private IPresentationContext fPresentationContext;
		private Object fViewerInput;
		private ITreeSelection fElements;
		private boolean fFirst = true;
		private IProgressMonitor fMonitor;

		public DetailJob(IPresentationContext context, Object viewerInput, ITreeSelection elements,
				IDebugModelPresentation model) {
			super("compute variable details"); //$NON-NLS-1$
			setSystem(true);
			fPresentationContext = context;
			fViewerInput = viewerInput;
			fElements = elements;
		}

		public IProgressMonitor getDetailMonitor() {
			return fMonitor;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
		 */
		@Override
		protected IStatus run(final IProgressMonitor monitor) {
			if (fMonitor != null && !fMonitor.isCanceled()) {
				fMonitor.setCanceled(true);
			}
			fMonitor = monitor;
			TreePath[] paths = fElements.getPaths();
			for (int i = 0; i < paths.length; i++) {
				if (monitor.isCanceled()) {
					break;
				}
				final TreePath path = paths[i];
				Object element = paths[i].getLastSegment();

				final IElementPropertiesProvider propertiesProvider = (IElementPropertiesProvider) DebugPlugin
						.getAdapter(element, IElementPropertiesProvider.class);

				Display display;
				if (fWorkbenchPartSite != null) {
					display = fWorkbenchPartSite.getShell().getDisplay();
				} else {
					display = PlatformUI.getWorkbench().getDisplay();
				}
				final Executor executor = SimpleDisplayExecutor.getSimpleDisplayExecutor(display);
				Set<String> properties = new HashSet<>(1);
				properties.add(IDebugVMConstants.PROP_FORMATTED_VALUE_AVAILABLE_FORMATS);
				propertiesProvider
						.update(new IPropertiesUpdate[] { new VMPropertiesUpdate(properties, path, fViewerInput,
								fPresentationContext, new DataRequestMonitor<Map<String, Object>>(executor, null) {
									@Override
									protected void handleCompleted() {

										// Bugzilla 310191: Detail pane does not clear up when DSF-GDB session is terminated
										if (fMonitor.isCanceled())
											return;

										Set<String> properties = new HashSet<>(1);
										properties.add(IElementPropertiesProvider.PROP_NAME);
										final String[] formats = (String[]) getData()
												.get(IDebugVMConstants.PROP_FORMATTED_VALUE_AVAILABLE_FORMATS);
										if (formats != null) {
											for (String format : formats) {
												properties
														.add(FormattedValueVMUtil.getPropertyForFormatId(format, null));
											}
										}

										propertiesProvider
												.update(new IPropertiesUpdate[] { new VMPropertiesUpdate(properties,
														path, fViewerInput, fPresentationContext,
														new DataRequestMonitor<Map<String, Object>>(executor, null) {
															@Override
															protected void handleSuccess() {
																StringBuilder finalResult = new StringBuilder();
																finalResult.append(NAME)
																		.append(getData().get(
																				IElementPropertiesProvider.PROP_NAME))
																		.append(CRLF);

																if (formats != null) {
																	for (int i = 0; i < formats.length; i++) {
																		String formatId = formats[i];
																		finalResult.append(SPACES);
																		finalResult.append(FormattedValueVMUtil
																				.getFormatLabel(formatId));
																		finalResult.append(FORMAT_SEPARATOR);
																		finalResult.append(
																				getData().get(FormattedValueVMUtil
																						.getPropertyForFormatId(
																								formatId, null)));
																		if (i < formats.length + 1) {
																			finalResult.append(CRLF);
																		}
																	}
																}
																detailComputed(null, finalResult.toString());
															}

															@Override
															protected void handleErrorOrWarning() {
																String rootMessage = new String(
																		getStatus().getMessage()).trim();
																// Provide a detail Error message to the user
																StringBuilder finalResult = new StringBuilder(
																		rootMessage);
																IStatus[] statuses = getStatus().getChildren();
																if (statuses != null) {
																	for (int i = 0; i < statuses.length; i++) {
																		String childMessage = statuses[i].getMessage()
																				.trim();
																		// Avoid root message duplication
																		if (!childMessage.equals(rootMessage)) {
																			finalResult.append(CRLF).append(CRLF)
																					.append(i + 1).append(PARENTHESES)
																					.append(childMessage);
																		}
																	}
																}

																detailComputed(null, finalResult.toString());
															}
														}) });
									}
								}) });
				continue;
			}

			return Status.OK_STATUS;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.core.runtime.jobs.Job#canceling()
		 */
		@Override
		protected void canceling() {
			super.canceling();
			synchronized (this) {
				notifyAll();
			}
		}

		/* (non-Javadoc)
		 * @see org.eclipse.debug.ui.IValueDetailListener#detailComputed(org.eclipse.debug.core.model.IValue, java.lang.String)
		 */
		@Override
		public void detailComputed(IValue value, final String result) {
			synchronized (this) {
			}
			if (!fMonitor.isCanceled()) {
				WorkbenchJob append = new WorkbenchJob("append details") { //$NON-NLS-1$
					@Override
					public IStatus runInUIThread(IProgressMonitor monitor) {
						if (!fMonitor.isCanceled()) {
							String insert = result;
							int length = 0;
							if (!fFirst) {
								length = getDetailDocument().getLength();
							}
							if (length > 0) {
								insert = CRLF + result;
							}
							try {
								int max = DsfUIPlugin.getDefault().getPreferenceStore()
										.getInt(IDebugUIConstants.PREF_MAX_DETAIL_LENGTH);
								if (max > 0 && insert.length() > max) {
									insert = insert.substring(0, max) + DOTS;
								}
								if (fFirst) {
									getDetailDocument().set(insert);
									fFirst = false;
								} else {
									getDetailDocument().replace(length, 0, insert);
								}
							} catch (BadLocationException e) {
								DsfUIPlugin.log(e);
							}
						}
						return Status.OK_STATUS;
					}
				};
				append.setSystem(true);
				append.schedule();
			}
			synchronized (this) {
				notifyAll();
			}
		}
	}

	/**
	 * The model presentation used to produce the string details for a
	 * selected variable.
	 */
	private String fDebugModelIdentifier;

	/**
	 * The text viewer in which the computed string detail
	 * of selected variables will be displayed.
	 */
	private TextViewer fTextViewer;

	/**
	 * The last selection displayed in the text viewer.
	 */
	private IStructuredSelection fLastDisplayed = null;

	/**
	 * Variables used to create the detailed information for a selection
	 */
	private IDocument fDetailDocument;
	private DetailJob fDetailJob = null;

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#createControl(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public Control createControl(Composite parent) {

		createTextViewer(parent);

		if (isInView()) {
			createViewSpecificComponents();
			createActions();
			DsfUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
			JFaceResources.getFontRegistry().addListener(this);
		}

		return fTextViewer.getControl();
	}

	/**
	 * Creates the text viewer in the given parent composite
	 *
	 * @param parent Parent composite to create the text viewer in
	 */
	private void createTextViewer(Composite parent) {

		// Create & configure a TextViewer
		fTextViewer = new TextViewer(parent, SWT.V_SCROLL | SWT.H_SCROLL);
		fTextViewer.setDocument(getDetailDocument());
		fTextViewer.getTextWidget().setFont(JFaceResources.getFont(IDebugUIConstants.PREF_DETAIL_PANE_FONT));
		fTextViewer.getTextWidget().setWordWrap(DsfUIPlugin.getDefault().getPreferenceStore()
				.getBoolean(IInternalDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP));
		fTextViewer.setEditable(false);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(fTextViewer.getTextWidget(),
				IDsfDebugHelpContextIds.DETAIL_PANE);
		Control control = fTextViewer.getControl();
		GridData gd = new GridData(GridData.FILL_BOTH);
		control.setLayoutData(gd);
	}

	/**
	 * Creates listeners and other components that should only be added to the
	 * text viewer when this detail pane is inside a view.
	 */
	private void createViewSpecificComponents() {

		// Add a document listener so actions get updated when the document changes
		getDetailDocument().addDocumentListener(new IDocumentListener() {
			@Override
			public void documentAboutToBeChanged(DocumentEvent event) {
			}

			@Override
			public void documentChanged(DocumentEvent event) {
				updateSelectionDependentActions();
			}
		});

		// Add the selection listener so selection dependent actions get updated.
		fTextViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() {
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				updateSelectionDependentActions();
			}
		});

		// Add a focus listener to update actions when details area gains focus
		fTextViewer.getControl().addFocusListener(new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent e) {

				setGlobalAction(IDebugView.SELECT_ALL_ACTION, getAction(DETAIL_SELECT_ALL_ACTION));
				setGlobalAction(IDebugView.COPY_ACTION, getAction(DETAIL_COPY_ACTION));

				getViewSite().getActionBars().updateActionBars();
			}

			@Override
			public void focusLost(FocusEvent e) {

				setGlobalAction(IDebugView.SELECT_ALL_ACTION, null);
				setGlobalAction(IDebugView.COPY_ACTION, null);

				getViewSite().getActionBars().updateActionBars();
			}
		});

		// Add a context menu to the detail area
		createDetailContextMenu(fTextViewer.getTextWidget());
	}

	/**
	 * Creates the actions to add to the context menu
	 */
	private void createActions() {

		TextViewerAction textAction = new TextViewerAction(fTextViewer, ITextOperationTarget.SELECT_ALL);
		textAction.configureAction(MessagesForDetailPane.DetailPane_Select_All, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
		textAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction,
				IDsfDebugHelpContextIds.DETAIL_PANE_SELECT_ALL_ACTION);
		setAction(DETAIL_SELECT_ALL_ACTION, textAction);

		textAction = new TextViewerAction(fTextViewer, ITextOperationTarget.COPY);
		textAction.configureAction(MessagesForDetailPane.DetailPane_Copy, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
		textAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(textAction, IDsfDebugHelpContextIds.DETAIL_PANE_COPY_ACTION);
		setAction(DETAIL_COPY_ACTION, textAction);

		setSelectionDependantAction(DETAIL_COPY_ACTION);

		updateSelectionDependentActions();

		IAction action = new DetailPaneWordWrapAction(fTextViewer);
		setAction(DETAIL_WORD_WRAP_ACTION, action);

		action = new DetailPaneMaxLengthAction(fTextViewer.getControl().getShell());
		setAction(DETAIL_MAX_LENGTH_ACTION, action);
	}

	/**
	 * Create the context menu particular to the detail pane.  Note that anyone
	 * wishing to contribute an action to this menu must use
	 * <code>IDebugUIConstants.VARIABLE_VIEW_DETAIL_ID</code> as the
	 * <code>targetID</code> in the extension XML.
	 */
	protected void createDetailContextMenu(Control menuControl) {
		MenuManager menuMgr = new MenuManager();
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			@Override
			public void menuAboutToShow(IMenuManager mgr) {
				fillDetailContextMenu(mgr);
			}
		});
		Menu menu = menuMgr.createContextMenu(menuControl);
		menuControl.setMenu(menu);

		getViewSite().registerContextMenu(IDebugUIConstants.VARIABLE_VIEW_DETAIL_ID, menuMgr,
				fTextViewer.getSelectionProvider());
	}

	/**
	* Adds items to the detail pane's context menu including any extension defined
	* actions.
	*
	* @param menu The menu to add the item to.
	*/
	protected void fillDetailContextMenu(IMenuManager menu) {

		menu.add(new Separator(IDebugUIConstants.VARIABLE_GROUP));
		menu.add(new Separator());
		menu.add(getAction(DETAIL_COPY_ACTION));
		menu.add(getAction(DETAIL_SELECT_ALL_ACTION));
		menu.add(new Separator());
		menu.add(getAction(DETAIL_WORD_WRAP_ACTION));
		menu.add(getAction(DETAIL_MAX_LENGTH_ACTION));
		menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#display(org.eclipse.jface.viewers.IStructuredSelection)
	 */
	@Override
	public void display(IStructuredSelection selection) {

		if (selection == null) {
			clearTextViewer();
			return;
		}

		fLastDisplayed = selection;

		if (selection.isEmpty() || !(selection instanceof ITreeSelection)) {
			clearTextViewer();
			return;
		}

		Object firstElement = selection.getFirstElement();
		if (firstElement instanceof IAdaptable) {
			IDebugModelProvider debugModelProvider = ((IAdaptable) firstElement).getAdapter(IDebugModelProvider.class);
			if (debugModelProvider != null) {
				String[] ids = debugModelProvider.getModelIdentifiers();
				if (ids != null && ids.length > 0) {
					setDebugModel(ids[0]);
				}
			}
		}

		synchronized (this) {
			if (fDetailJob != null) {
				fDetailJob.cancel();
			}
			if (fWorkbenchPartSite != null) {
				IWorkbenchPart part = fWorkbenchPartSite.getPart();
				if (part instanceof IDebugView) {
					Viewer viewer = ((IDebugView) part).getViewer();
					Object input = viewer.getInput();
					if (input != null && viewer instanceof TreeModelViewer) {
						TreeModelViewer treeModelViewer = (TreeModelViewer) viewer;
						fDetailJob = new DetailJob(treeModelViewer.getPresentationContext(), input,
								(ITreeSelection) selection, null);
						fDetailJob.schedule();
					}
				}
			} else if (firstElement instanceof IDMVMContext) {
				IVMNode vmNode = ((IDMVMContext) firstElement).getVMNode();
				if (vmNode != null) {
					Object input = firstElement;
					IVMProvider vmProvider = vmNode.getVMProvider();
					final IPresentationContext context = vmProvider.getPresentationContext();
					if (IDsfDebugUIConstants.ID_EXPRESSION_HOVER.equals(context.getId())) {
						// magic access to viewer input - see ExpressionVMProvider
						input = context.getProperty("__viewerInput"); //$NON-NLS-1$
					}
					fDetailJob = new DetailJob(context, input, (ITreeSelection) selection, null);
					fDetailJob.schedule();
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#setFocus()
	 */
	@Override
	public boolean setFocus() {
		if (fTextViewer != null) {
			fTextViewer.getTextWidget().setFocus();
			return true;
		}
		return false;
	}

	/*
	 *
	 */
	@Override
	public void dispose() {
		fActionMap.clear();
		fSelectionActions.clear();

		if (fDetailJob != null)
			fDetailJob.cancel();
		fDebugModelIdentifier = null; // Setting this to null makes sure the text viewer is reconfigured with the model presentation after disposal
		if (fTextViewer != null && fTextViewer.getControl() != null) {
			fTextViewer.getControl().dispose();
			fTextViewer = null;
		}

		if (isInView()) {
			DsfUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
			JFaceResources.getFontRegistry().removeListener(this);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#getDescription()
	 */
	@Override
	public String getDescription() {
		return MessagesForDetailPane.NumberFormatDetailPane_Description;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#getID()
	 */
	@Override
	public String getID() {
		return ID;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.IDetailPane#getName()
	 */
	@Override
	public String getName() {
		return MessagesForDetailPane.NumberFormatDetailPane_Name;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public <T> T getAdapter(Class<T> required) {
		if (IFindReplaceTarget.class.equals(required)) {
			return (T) fTextViewer.getFindReplaceTarget();
		}
		if (ITextViewer.class.equals(required)) {
			return (T) fTextViewer;
		}
		return null;
	}

	/**
	 * Lazily instantiate and return a Document for the detail pane text viewer.
	 */
	protected IDocument getDetailDocument() {
		if (fDetailDocument == null) {
			fDetailDocument = new Document();
		}
		return fDetailDocument;
	}

	/**
	 * Clears the text viewer, removes all text.
	 */
	protected void clearTextViewer() {
		if (fDetailJob != null) {
			fDetailJob.cancel();

			// Bugzilla 310191: Detail pane does not clear up when DSF-GDB session is terminated
			IProgressMonitor progressMonitor = fDetailJob.getDetailMonitor();
			if (progressMonitor != null)
				progressMonitor.setCanceled(true);
		}
		fLastDisplayed = null;
		fDetailDocument.set(""); //$NON-NLS-1$
		fTextViewer.setEditable(false);
	}

	/**
	 * Configures the details viewer for the debug model
	 * currently being displayed
	 */
	protected void configureDetailsViewer() {
		fTextViewer.setEditable(false);
	}

	/**
	 * Returns the identifier of the debug model being displayed
	 * in this view, or <code>null</code> if none.
	 *
	 * @return debug model identifier
	 */
	protected String getDebugModel() {
		return fDebugModelIdentifier;
	}

	/**
	 * Sets the identifier of the debug model being displayed
	 * in this view, or <code>null</code> if none.
	 *
	 * @param id debug model identifier of the type of debug
	 *  elements being displayed in this view
	 */
	protected void setDebugModel(String id) {
		if (!id.equals(fDebugModelIdentifier)) {
			fDebugModelIdentifier = id;
			configureDetailsViewer();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
	 */
	@Override
	public void propertyChange(PropertyChangeEvent event) {
		String propertyName = event.getProperty();
		if (propertyName.equals(IDebugUIConstants.PREF_DETAIL_PANE_FONT)) {
			fTextViewer.getTextWidget().setFont(JFaceResources.getFont(IDebugUIConstants.PREF_DETAIL_PANE_FONT));
		} else if (propertyName.equals(IDebugUIConstants.PREF_MAX_DETAIL_LENGTH)) {
			display(fLastDisplayed);
		} else if (propertyName.equals(IInternalDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP)) {
			fTextViewer.getTextWidget().setWordWrap(DsfUIPlugin.getDefault().getPreferenceStore()
					.getBoolean(IInternalDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP));
			getAction(DETAIL_WORD_WRAP_ACTION).setChecked(DsfUIPlugin.getDefault().getPreferenceStore()
					.getBoolean(IInternalDsfDebugUIConstants.PREF_DETAIL_PANE_WORD_WRAP));
		}
	}

	/*
	 * @see org.eclipse.debug.ui.IDetailPane2#getSelectionProvider()
	 */
	@Override
	public ISelectionProvider getSelectionProvider() {
		if (fTextViewer != null) {
			return fTextViewer.getSelectionProvider();
		}
		return null;
	}
}
