/*******************************************************************************
 * Copyright (c) 2001, 2009 IBM Corporation and others.
 * All rights reserved. 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
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.sse.ui.internal.extension;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorExtension2;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager;
import org.eclipse.wst.sse.ui.internal.IExtendedSimpleEditor;
import org.eclipse.wst.sse.ui.internal.Logger;
import org.eclipse.wst.sse.ui.internal.provisional.extensions.ISelfValidateEditAction;


/**
 */
public class DropActionProxy implements InvocationHandler {
	public static Object newInstance(Object obj) {
		Object instance = null;
		try {
			Set set = new HashSet();
			Class clazz = obj.getClass();
			while (clazz != null) {
				Class[] interfaces = clazz.getInterfaces();
				for (int i = 0; i < interfaces.length; i++) {
					set.add(interfaces[i]);
				}
				clazz = clazz.getSuperclass();
			}
			Class[] classes = new Class[set.size()];
			Iterator itr = set.iterator();
			int i = 0;
			while (itr.hasNext()) {
				classes[i] = (Class) itr.next();
				i++;
			}
			instance = Proxy.newProxyInstance(obj.getClass().getClassLoader(), classes, new DropActionProxy(obj));
		} catch (Error e) {
			Logger.logException("Exception while proxying a drop action", e); //$NON-NLS-1$
			instance = obj;
		}
		return instance;
	}

	private IExtendedSimpleEditor editor = null;
	private IStructuredModel fRecorder;
	private Object obj;

	private DropActionProxy(Object obj) {
		this.obj = obj;
	}

	private void beginRecording() {
		IDocument document = null;
		if (editor != null) {
			document = editor.getDocument();
			if (document != null)
				fRecorder = StructuredModelManager.getModelManager().getExistingModelForEdit(document);
			// Prepare for Undo
			if (fRecorder != null) {
				IStructuredTextUndoManager um = fRecorder.getUndoManager();
				if (um != null) {
					if (this.obj instanceof IAction)
						um.beginRecording(this, ((IAction) this.obj).getText(), ((IAction) this.obj).getDescription());
					else
						um.beginRecording(this);
				}
			}
		}
	}

	private void endRecording() {
		if (fRecorder != null) {
			IStructuredTextUndoManager um = fRecorder.getUndoManager();
			if (um != null)
				um.endRecording(this);
			fRecorder.releaseFromEdit();
			fRecorder = null;
		}
	}

	/**
	 * @see java.lang.reflect.InvocationHandler#invoke(Object, Method,
	 *      Object[])
	 */
	public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
		Object result;
		String name = m.getName();
		try {
			if (name.equals("equals")) { //$NON-NLS-1$
				// Workaround for JDK's bug 4652876
				// "equals" always returns false even if both
				// InvocationHandler
				// class
				// hold the same objects
				// See
				// http://developer.java.sun.com/developer/bugParade/bugs/4652876.html
				// This problem is in the IBM SDK 1.3.1
				// but I don't see the bug in Sun's JDK 1.4.1 (beta)
				Object arg = args[0];
				return (proxy.getClass() == arg.getClass() && equals(Proxy.getInvocationHandler(arg))) ? Boolean.TRUE : Boolean.FALSE;
			} else if (name.equals("run")) { //$NON-NLS-1$
				if (args[1] instanceof IExtendedSimpleEditor) {
					editor = (IExtendedSimpleEditor) args[1];
				}
				beginRecording();
				if ((args[1] instanceof ITextEditor) && !(obj instanceof ISelfValidateEditAction) && !isEditable((ITextEditor) args[1])) {
					return Boolean.FALSE;
				}
			}
			result = m.invoke(obj, args);
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		} finally {
			if (name.equals("run")) { //$NON-NLS-1$
				endRecording();
			}
		}
		return result;
	}

	private boolean isEditable(ITextEditor editor) {
		if (editor instanceof ITextEditorExtension2)
			return ((ITextEditorExtension2) editor).validateEditorInputState();
		return editor.isEditable();
	}
}
