/*******************************************************************************
 * Copyright (c) 2000, 2011 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.jdt.internal.ui.packageview;

import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;

import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.jface.viewers.StructuredViewer;

import org.eclipse.ui.actions.CopyFilesAndFoldersOperation;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;

import org.eclipse.jdt.internal.corext.util.Resources;

import org.eclipse.jdt.internal.ui.dnd.JdtViewerDropAdapter;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;

/**
 * Adapter to handle file drop from other applications.
 */
public class FileTransferDropAdapter extends JdtViewerDropAdapter implements TransferDropTargetListener {

	public FileTransferDropAdapter(StructuredViewer viewer) {
		super(viewer);

		setScrollEnabled(true);
		setExpandEnabled(true);
		setFeedbackEnabled(false);
	}

	//---- TransferDropTargetListener interface ---------------------------------------

	@Override
	public Transfer getTransfer() {
		return FileTransfer.getInstance();
	}

	@Override
	public boolean isEnabled(DropTargetEvent event) {
		Object target= event.item != null ? event.item.getData() : null;
		if (target == null)
			return false;
		return target instanceof IJavaElement || target instanceof IResource;
	}

	//---- Actual DND -----------------------------------------------------------------

	@Override
	public boolean validateDrop(Object target, int operation, TransferData transferType) {
		return determineOperation(target, operation, transferType, DND.DROP_MOVE | DND.DROP_LINK | DND.DROP_COPY) != DND.DROP_NONE;
	}

	@Override
	protected int determineOperation(Object target, int operation, TransferData transferType, int operations) {

		boolean isPackageFragment= target instanceof IPackageFragment;
		boolean isJavaProject= target instanceof IJavaProject;
		boolean isPackageFragmentRoot= target instanceof IPackageFragmentRoot;
		boolean isContainer= target instanceof IContainer;

		if (!isPackageFragment && !isJavaProject && !isPackageFragmentRoot && !isContainer)
			return DND.DROP_NONE;

		if (isContainer) {
			IContainer container= (IContainer)target;
			if (container.isAccessible() && !Resources.isReadOnly(container))
				return DND.DROP_COPY;
		} else {
			IJavaElement element= (IJavaElement)target;
			if (!element.isReadOnly())
				return DND.DROP_COPY;
		}

		return DND.DROP_NONE;
	}

	@Override
	public boolean performDrop(final Object data) {
		try {
			final int currentOperation= getCurrentOperation();

			if (data == null || !(data instanceof String[]) || currentOperation != DND.DROP_COPY)
				return false;

			final IContainer target= getActualTarget(getCurrentTarget());
			if (target == null)
				return false;

			// Run the import operation asynchronously.
			// Otherwise the drag source (e.g., Windows Explorer) will be blocked
			// while the operation executes. Fixes bug 35796.
			Display.getCurrent().asyncExec(() -> {
				getShell().forceActive();
				new CopyFilesAndFoldersOperation(getShell()).copyOrLinkFiles((String[])data, target, currentOperation);
			});

			return true;
		} catch (JavaModelException e) {
			String title= PackagesMessages.DropAdapter_errorTitle;
			String message= PackagesMessages.DropAdapter_errorMessage;
			ExceptionHandler.handle(e, getShell(), title, message);
			return false;
		}
	}

	private IContainer getActualTarget(Object dropTarget) throws JavaModelException{
		if (dropTarget instanceof IContainer)
			return (IContainer)dropTarget;
		else if (dropTarget instanceof IJavaElement)
			return getActualTarget(((IJavaElement)dropTarget).getCorrespondingResource());
		return null;
	}

	private Shell getShell() {
		return getViewer().getControl().getShell();
	}
}
