/*******************************************************************************
 * Copyright (c) 2000, 2008 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.internal.editors.quickdiff;

import java.util.ResourceBundle;

import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;

import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IChangeRulerColumn;
import org.eclipse.jface.text.source.ILineDiffer;
import org.eclipse.jface.text.source.IVerticalRulerInfo;

import org.eclipse.ui.IEditorInput;

import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IEditorStatusLine;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.TextEditorAction;

/**
 * Abstract superclass of actions that restore / revert parts of a document displayed in the action's
 * editor to the state described by the {@link ILineDiffer ILineDiffer} associated with the document's
 * {@link IAnnotationModel IAnnotationModel}.
 *
 * @since 3.1
 */
public abstract class QuickDiffRestoreAction extends TextEditorAction implements ISelectionChangedListener {

	private int fLastLine= -1;
	private final boolean fIsRulerAction;

	/**
	 * Creates a new instance.
	 *
	 * @param bundle the resource bundle
	 * @param prefix a prefix to be prepended to the various resource keys
	 * @param editor the editor this action belongs to
	 * @param isRulerAction <code>true</code> if this is a ruler action
	 */
	QuickDiffRestoreAction(ResourceBundle bundle, String prefix, ITextEditor editor, boolean isRulerAction) {
		super(bundle, prefix, editor);
		fIsRulerAction= isRulerAction;

		ISelectionProvider selectionProvider= editor.getSelectionProvider();
		if (selectionProvider instanceof IPostSelectionProvider)
			((IPostSelectionProvider)selectionProvider).addPostSelectionChangedListener(this);
	}

	/**
	 * Called by this action's run method inside a pair of calls to <code>IRewriteTarget.beginCompoundChange</code>
	 * and <code>IRewriteTarget.endCompoundChange</code>().
	 *
	 * @see IRewriteTarget
	 */
	protected abstract void runCompoundChange();

	@Override
	public void run() {
		ITextEditor editor= getTextEditor();
		if (editor == null || !validateEditorInputState())
			return;
		IRewriteTarget target= editor.getAdapter(IRewriteTarget.class);
		if (target != null)
			target.beginCompoundChange();
		runCompoundChange();
		if (target != null)
			target.endCompoundChange();

	}

	@Override
	public void update() {
		/*
		 * Update only works if we're updated from the ruler action
		 * (see AbstractDecoratedTextEditor.rulerContextMenuAboutToShow).
		 */
		super.update();

		setEnabled(computeEnablement());
	}

	@Override
	public void selectionChanged(SelectionChangedEvent event) {
		update();
	}

	/**
	 * Computes, caches and returns the internal state, including enablement.
	 *
	 * @return <code>true</code> if the action is enabled, <code>false</code>
	 *         if it is not
	 */
	protected boolean computeEnablement() {
		if (!super.isEnabled())
			return false;

		if (!canModifyEditor())
			return false;

		fLastLine= computeLine(fIsRulerAction);
		return true;
	}

	/**
	 * Returns the selection of the editor this action belongs to.
	 *
	 * @return the editor's selection, or <code>null</code>
	 */
	protected ITextSelection getSelection() {
		if (getTextEditor() == null)
			return null;
		ISelectionProvider sp= getTextEditor().getSelectionProvider();
		if (sp == null)
			return null;
		ISelection s= sp.getSelection();
		if (s instanceof ITextSelection)
			return (ITextSelection)s;
		return null;
	}

	/**
	 * Returns the current line of activity
	 *
	 * @return the currently active line
	 * @since 3.1
	 */
	protected int getLastLine() {
		return fLastLine;
	}

	/**
	 * Returns the active line
	 *
	 * @param useRulerInfo <code>true</code> if the ruler info should be used
	 * @return the line of interest.
	 * @since 3.1
	 */
	private int computeLine(boolean useRulerInfo) {
		int lastLine;
		if (useRulerInfo) {
			IVerticalRulerInfo ruler= getRuler();
			if (ruler == null)
				lastLine= -1;
			else
				lastLine= ruler.getLineOfLastMouseButtonActivity();
		} else {
			ITextSelection selection= getSelection();
			if (selection == null)
				lastLine= -1;
			else
				lastLine= selection.getEndLine();
		}
		return lastLine;
	}

	/**
	 * Returns the annotation model of the document displayed in this action's editor, if it
	 * implements the {@link IAnnotationModelExtension IAnnotationModelExtension} interface.
	 *
	 * @return the displayed document's annotation model if it is an <code>IAnnotationModelExtension</code>, or <code>null</code>
	 */
	private IAnnotationModelExtension getModel() {
		if (getTextEditor() == null)
			return null;
		IDocumentProvider provider= getTextEditor().getDocumentProvider();
		IEditorInput editorInput= getTextEditor().getEditorInput();
		IAnnotationModel m= provider.getAnnotationModel(editorInput);
		if (m instanceof IAnnotationModelExtension)
			return (IAnnotationModelExtension)m;
		return null;
	}

	/**
	 * Returns the diff model associated with the annotation model of the document currently displayed
	 * in this action's editor, if any.
	 *
	 * @return the diff model associated with the displayed document, or <code>null</code>
	 */
	protected ILineDiffer getDiffer() {
		IAnnotationModelExtension extension= getModel();
		if (extension != null)
			return (ILineDiffer)extension.getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
		return null;
	}

	/**
	 * Returns a <code>IVerticalRulerInfo</code> if this action's editor adapts to one.
	 *
	 * @return the <code>IVerticalRulerInfo</code> for the editor's vertical ruler, or <code>null</code>
	 */
	protected IVerticalRulerInfo getRuler() {
		if (getTextEditor() != null)
			return getTextEditor().getAdapter(IVerticalRulerInfo.class);
		return null;
	}

	/**
	 * Sets the status line error message to <code>string</code>.
	 *
	 * @param string the message to be displayed as error.
	 */
	protected void setStatus(String string) {
		if (getTextEditor() != null) {
			IEditorStatusLine statusLine= getTextEditor().getAdapter(IEditorStatusLine.class);
			if (statusLine != null) {
				statusLine.setMessage(true, string, null);
			}
		}
	}
}
