/*******************************************************************************
 * Copyright (c) 2000, 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 Corporation - initial API and implementation
 *******************************************************************************/


package org.eclipse.ui.texteditor;


import java.util.ResourceBundle;

import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import org.eclipse.jface.text.ITextOperationTarget;

import org.eclipse.ui.IWorkbenchPartSite;



/**
 * Action for shifting code to the right or left by one indentation level.
 * @since 2.0
 */
public class ShiftAction extends TextEditorAction implements IReadOnlyDependent {

	/** The text operation code */
	private int fOperationCode= -1;
	/** The text operation target */
	private ITextOperationTarget fOperationTarget;

	/**
	 * Creates and initializes the action for the given text editor and operation
	 * code. The action configures its visual representation from the given resource
	 * bundle. The action works by asking the text editor at the time for its
	 * text operation target adapter (using
	 * <code>getAdapter(ITextOperationTarget.class)</code>. The action runs that
	 * operation with the given opcode.
	 *
	 * @param bundle the resource bundle
	 * @param prefix a prefix to be prepended to the various resource keys
	 *   (described in <code>ResourceAction</code> constructor), or  <code>null</code> if none
	 * @param editor the text editor
	 * @param operationCode the operation code
	 * @see TextEditorAction#TextEditorAction(ResourceBundle, String, ITextEditor)
	 */
	public ShiftAction(ResourceBundle bundle, String prefix, ITextEditor editor, int operationCode) {
		super(bundle, prefix, editor);
		fOperationCode= operationCode;
		update();
	}

	/**
	 * The <code>TextOperationAction</code> implementation of this
	 * <code>IAction</code> method runs the operation with the current
	 * operation code.
	 */
	@Override
	public void run() {
		if (fOperationCode == -1 || fOperationTarget == null)
			return;

		ITextEditor editor= getTextEditor();
		if (editor == null)
			return;

		if (!validateEditorInputState())
			return;

		Display display= null;

		IWorkbenchPartSite site= editor.getSite();
		Shell shell= site.getShell();
		if (shell != null && !shell.isDisposed())
			display= shell.getDisplay();

		BusyIndicator.showWhile(display, () -> fOperationTarget.doOperation(fOperationCode));
	}

	@Override
	public void update() {
		super.update();
		if (!isEnabled())
			return;

		if (!canModifyEditor()) {
			setEnabled(false);
			return;
		}

		ITextEditor editor= getTextEditor();
		if (fOperationTarget == null && editor != null && fOperationCode != -1)
			fOperationTarget= editor.getAdapter(ITextOperationTarget.class);

	}

	/**
	 * Enablement when tab key is pressed - the current selection has to be cover multiple lines.
	 *
	 * @since 3.0
	 */
	protected void updateForTab() {
		super.update();

		if (isEnabled()) {
			if (!canModifyEditor()) {
				setEnabled(false);
				return;
			}

			ITextEditor editor= getTextEditor();
			if (fOperationTarget == null && editor != null && fOperationCode != -1)
				fOperationTarget= editor.getAdapter(ITextOperationTarget.class);

			boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
			setEnabled(isEnabled);
		}

	}

	@Override
	public void setEditor(ITextEditor editor) {
		super.setEditor(editor);
		fOperationTarget= null;
	}

	@Override
	public boolean isEnabled(boolean isWritable) {

		if (!isWritable)
			return false;

		/*
		 * Note that this implementation still honors the result returned by canDoOperation.
		 * I.e. if the viewer is set to read-only, this method still returns false.
		 * It covers the case in which the viewer is also writable.
		 *
		 */
		ITextEditor editor= getTextEditor();
		if (fOperationTarget == null && editor!= null && fOperationCode != -1)
			fOperationTarget= editor.getAdapter(ITextOperationTarget.class);

		return (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
	}
}
