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

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IDebugElement;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.ui.JavaDebugUtils;
import org.eclipse.jdt.internal.debug.core.model.JDIThread;

/**
 * Java thread presentation adapter.
 *
 * @since 3.3
 */
public class JavaThreadContentProvider extends JavaElementContentProvider {

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.viewers.model.provisional.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext)
	 */
	@Override
	protected int getChildCount(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException {
		IJavaThread thread = (IJavaThread)element;
		if (!thread.isSuspended()) {
			return 0;
		}
		int childCount = thread.getFrameCount();
		if (isDisplayMonitors()) {
			if (((IJavaDebugTarget) thread.getDebugTarget()).supportsMonitorInformation()) {
				childCount+= thread.getOwnedMonitors().length;
				if (thread.getContendedMonitor() != null) {
					childCount++;
				}
			} else {
				// unavailable notice
				childCount++;
			}
		}
		return childCount;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, org.eclipse.core.runtime.IProgressMonitor)
	 */
	@Override
	protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context, IViewerUpdate monitor) throws CoreException {
		IJavaThread thread = (IJavaThread)parent;
		if (!thread.isSuspended()) {
			return EMPTY;
		}
		return getElements(getChildren(thread), index, length);
	}

	protected Object[] getChildren(IJavaThread thread) {
		try {
			if (thread instanceof JDIThread) {
				JDIThread jThread = (JDIThread) thread;
				if (!jThread.getDebugTarget().isSuspended() ) {
					if (jThread.isSuspendVoteInProgress()) {
						return EMPTY;
					}
				}
			}
			IStackFrame[] frames = thread.getStackFrames();
			if (!isDisplayMonitors()) {
				return frames;
			}

			Object[] children;
			int length = frames.length;
			if (((IJavaDebugTarget) thread.getDebugTarget()).supportsMonitorInformation()) {
				IDebugElement[] ownedMonitors = JavaDebugUtils.getOwnedMonitors(thread);
				IDebugElement contendedMonitor = JavaDebugUtils.getContendedMonitor(thread);
				length += ownedMonitors.length;
				if (contendedMonitor != null) {
					length++;
				}
				children = new Object[length];
				if (ownedMonitors.length > 0) {
					System.arraycopy(ownedMonitors, 0, children, 0, ownedMonitors.length);
				}
				if (contendedMonitor != null) {
					// Insert the contended monitor after the owned monitors
					children[ownedMonitors.length] = contendedMonitor;
				}
			} else {
				children = new Object[length + 1];
				children[0] = new NoMonitorInformationElement(thread.getDebugTarget());
			}
			int offset = children.length - frames.length;
			System.arraycopy(frames, 0, children, offset, frames.length);
			return children;
		} catch (DebugException e) {
			return EMPTY;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#hasChildren(java.lang.Object, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, org.eclipse.core.runtime.IProgressMonitor)
	 */
	@Override
	protected boolean hasChildren(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException {
		if (element instanceof JDIThread) {
			JDIThread jThread = (JDIThread) element;
			if (!jThread.getDebugTarget().isSuspended()) {
				if (jThread.isSuspendVoteInProgress()) {
					return false;
				}
			}
		}
		return ((IJavaThread)element).hasStackFrames() ||
			(isDisplayMonitors() && ((IJavaThread)element).hasOwnedMonitors());
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getRule(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate[])
	 */
	@Override
	protected ISchedulingRule getRule(IChildrenCountUpdate[] updates) {
		return getThreadRule(updates);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getRule(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate[])
	 */
	@Override
	protected ISchedulingRule getRule(IChildrenUpdate[] updates) {
		return getThreadRule(updates);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getRule(org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate[])
	 */
	@Override
	protected ISchedulingRule getRule(IHasChildrenUpdate[] updates) {
		return getThreadRule(updates);
	}

	/**
	 * Returns a scheduling rule to ensure we aren't trying to get thread content
	 * while executing an implicit evaluation (like toString() for the details
	 * pane).
	 *
	 * @param updates viewer updates
	 * @return scheduling rule or <code>null</code>
	 */
	private ISchedulingRule getThreadRule(IViewerUpdate[] updates) {
		if (updates.length > 0) {
			Object element = updates[0].getElement();
			if (element instanceof JDIThread) {
				return ((JDIThread)element).getThreadRule();
			}
		}
		return null;
	}


}
