package ccw.util;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.commands.IHandler;
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.ExpressionInfo;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;

/**
 * Largely inspired by David Green:
 * http://greensopinion.blogspot.fr/2009/10/key-bindings-in-eclipse-editors.html
 */
public class TextViewerSupport implements FocusListener, DisposeListener {

	private final TextViewer textViewer;
	private final IHandlerService handlerService;
	private final List<IHandlerActivation> handlerActivations = 
			new ArrayList<IHandlerActivation>();

	public TextViewerSupport(TextViewer textViewer,
			IHandlerService handlerService) {
		this.textViewer = textViewer;
		this.handlerService = handlerService;
		StyledText textWidget = textViewer.getTextWidget();
		textWidget.addFocusListener(this);
		textWidget.addDisposeListener(this);

		if (textViewer.getTextWidget().isFocusControl()) {
			activateContext();
		}
	}

	public void focusLost(FocusEvent e) {
		deactivateContext();
	}

	public void focusGained(FocusEvent e) {
		activateContext();
	}

	public void widgetDisposed(DisposeEvent e) {
		deactivateContext();
	}

	protected void activateContext() {
		if (handlerActivations.isEmpty()) {
			activateHandler(ISourceViewer.QUICK_ASSIST,
					ITextEditorActionDefinitionIds.QUICK_ASSIST);
			activateHandler(ISourceViewer.CONTENTASSIST_PROPOSALS,
					ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
			activateHandler(ITextOperationTarget.CUT,
					IWorkbenchCommandConstants.EDIT_CUT);
			activateHandler(ITextOperationTarget.COPY,
					IWorkbenchCommandConstants.EDIT_COPY);
			activateHandler(ITextOperationTarget.PASTE,
					IWorkbenchCommandConstants.EDIT_PASTE);
			activateHandler(ITextOperationTarget.DELETE,
					IWorkbenchCommandConstants.EDIT_DELETE);
			activateHandler(ITextOperationTarget.UNDO,
					IWorkbenchCommandConstants.EDIT_UNDO);
			activateHandler(ITextOperationTarget.REDO,
					IWorkbenchCommandConstants.EDIT_REDO);
			activateHandler(ITextOperationTarget.SELECT_ALL,
					IWorkbenchCommandConstants.EDIT_SELECT_ALL);
			activateHandler(ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION,
					IWorkbenchCommandConstants.EDIT_CONTEXT_INFORMATION);
			// activateHandler(ITextViewer,
			// IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
		}
	}

	protected void activateHandler(int operation, String actionDefinitionId) {
		StyledText textWidget = textViewer.getTextWidget();
		IHandler actionHandler = createActionHandler(operation,
				actionDefinitionId);
		IHandlerActivation handlerActivation = handlerService.activateHandler(
				actionDefinitionId, actionHandler,
				new ActiveFocusControlExpression(textWidget));

		handlerActivations.add(handlerActivation);
	}

	private IHandler createActionHandler(final int operation,
			String actionDefinitionId) {
		Action action = new Action() {
			@Override
			public void run() {
				if (textViewer.canDoOperation(operation)) {
					textViewer.doOperation(operation);
				}
			}
		};
		action.setActionDefinitionId(actionDefinitionId);
		return new ActionHandler(action);
	}

	protected void deactivateContext() {
		if (!handlerActivations.isEmpty()) {
			for (IHandlerActivation activation : handlerActivations) {
				handlerService.deactivateHandler(activation);
				activation.getHandler().dispose();
			}
			handlerActivations.clear();
		}
	}

	/**
	 * An expression that evaluates to true if and only if the current focus
	 * control is the one provided. Has a very high priority in order to ensure
	 * proper conflict resolution.
	 */
	public class ActiveFocusControlExpression extends Expression {

		private Control focusControl;

		public ActiveFocusControlExpression(Control control) {
			focusControl = control;
		}

		/*
		 * In collectExpressionInfo we ensure that the expression indicates that
		 * it uses the default variable. This gives the expression a very high
		 *  priority. Since the expression is only enabled when our control has 
		 *  focus, we've ensured that undo is directed to our text control at 
		 *  the right time.
		 */
		@Override
		public void collectExpressionInfo(ExpressionInfo info) {
			info.markDefaultVariableAccessed(); // give it a very high priority
			info.addVariableNameAccess(ISources.ACTIVE_SHELL_NAME);
			info.addVariableNameAccess(ISources.ACTIVE_WORKBENCH_WINDOW_NAME);
		}

		@Override
		public EvaluationResult evaluate(IEvaluationContext context)
				throws CoreException {
			if (Display.getCurrent() != null && focusControl.isFocusControl()) {
				return EvaluationResult.TRUE;
			}
			return EvaluationResult.FALSE;
		}
	}
}
@@ -2671,24 +2671,117 @@
 DIT_PASTE);%0A
+%09%09%09// We don't add DELETE, because if we do so, then the DELETE%0A%09%09%09// is done twice ...%0A%09%09%09//
 %09%09%09activateH
@@ -2812,24 +2812,29 @@
 get.DELETE,%0A
+%09%09%09//
 %09%09%09%09%09IWorkbe

Fix Issue 461: Pressing delete key in REPL deletes 2 characters