/*******************************************************************************
 * Copyright (c) 2010, 2018 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 - Initial API and implementation
 *******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;


import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;

/**
 * The TimeTriggeredProgressMonitorDialog is a progress monitor dialog that only
 * opens if the runnable provided exceeds the specified long operation time.
 *
 * @since 3.6 - Copied from org.eclipse.ui.internal.operations.TimeTriggeredProgressMonitorDialog
 */
public class TimeTriggeredProgressMonitorDialog extends ProgressMonitorDialog {

	/**
	 * The time considered to be the long operation time.
	 */
	private final int longOperationTime;

	/**
	 * The time at which the dialog should be opened.
	 */
	private long triggerTime = -1;

	/**
	 * Whether or not we've already opened a dialog.
	 */
	private boolean dialogOpened = false;

	/**
	 * Wrappered monitor so we can check ticks and open the dialog when
	 * appropriate
	 */
	private IProgressMonitor wrapperedMonitor;

	/**
	 * Create a new instance of the receiver.
	 *
	 * @param parent
	 *            the parent of the dialog
	 * @param longOperationTime
	 *            the time (in milliseconds) considered to be a long enough
	 *            execution time to warrant opening a dialog.
	 */
	public TimeTriggeredProgressMonitorDialog(Shell parent,
			int longOperationTime) {
		super(parent);
		setOpenOnRun(false);
		this.longOperationTime = longOperationTime;
	}

	/**
	 * Create a monitor for the receiver that wrappers the superclasses monitor.
	 *
	 */
	public void createWrapperedMonitor() {
		wrapperedMonitor = new IProgressMonitor() {

			IProgressMonitor superMonitor = TimeTriggeredProgressMonitorDialog.super
					.getProgressMonitor();

			@Override
			public void beginTask(String name, int totalWork) {
				superMonitor.beginTask(name, totalWork);
				checkTicking();
			}

			/**
			 * Check if we have ticked in the last 800ms.
			 */
			private void checkTicking() {
				if (triggerTime < 0) {
					triggerTime = System.currentTimeMillis() + longOperationTime;
				}
				if (!dialogOpened && System.currentTimeMillis() > triggerTime) {
					open();
					dialogOpened = true;
				}
			}

			@Override
			public void done() {
				superMonitor.done();
				checkTicking();
			}

			@Override
			public void internalWorked(double work) {
				superMonitor.internalWorked(work);
				checkTicking();
			}

			@Override
			public boolean isCanceled() {
				return superMonitor.isCanceled();
			}

			@Override
			public void setCanceled(boolean value) {
				superMonitor.setCanceled(value);

			}

			@Override
			public void setTaskName(String name) {
				superMonitor.setTaskName(name);
				checkTicking();

			}

			@Override
			public void subTask(String name) {
				superMonitor.subTask(name);
				checkTicking();
			}

			@Override
			public void worked(int work) {
				superMonitor.worked(work);
				checkTicking();

			}
		};
	}

	@Override
	public IProgressMonitor getProgressMonitor() {
		if (wrapperedMonitor == null) {
			createWrapperedMonitor();
		}
		return wrapperedMonitor;
	}

	@Override
	public void run(final boolean fork, final boolean cancelable,
			final IRunnableWithProgress runnable) throws InvocationTargetException,
			InterruptedException {
		final InvocationTargetException[] invokes = new InvocationTargetException[1];
		final InterruptedException[] interrupt = new InterruptedException[1];
		Runnable dialogWaitRunnable = () -> {
			try {
				TimeTriggeredProgressMonitorDialog.super.run(fork, cancelable, runnable);
			} catch (InvocationTargetException e1) {
				invokes[0] = e1;
			} catch (InterruptedException e2) {
				interrupt[0] = e2;
			}
		};
		final Display display = PlatformUI.getWorkbench().getDisplay();
		if (display == null) {
			return;
		}
		//show a busy cursor until the dialog opens
		BusyIndicator.showWhile(display, dialogWaitRunnable);
		if (invokes[0] != null) {
			throw invokes[0];
		}
		if (interrupt[0] != null) {
			throw interrupt[0];
		}
	 }
}
