/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.ui.dnd;

import com.mucommander.commons.file.AbstractFile;
import com.mucommander.commons.file.util.FileSet;
import com.mucommander.ui.main.FolderPanel;
import com.mucommander.ui.main.table.FileTable;
import com.mucommander.ui.main.table.FileTableModel;

import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.InputEvent;


/**
 * This class adds 'drag' support to components that are registered using the {@link #enableDrag(java.awt.Component)}
 * method.
 *
 * <p>A {@link com.mucommander.ui.main.FolderPanel} instance has to be specified at creation time, this instance will be
 * used to retrieve the list of selected/marked file(s) that are dragged, whenever a drag operation is initiated on
 * of the registered components.
 *
 * @author Maxence Bernard
 */
public class FileDragSourceListener implements DragGestureListener, DragSourceListener {

    /** the FolderPanel instance used to retrieve dragged files */
    private FolderPanel folderPanel;


    /**
     * Creates a new FileDragSourceListener using the specified FolderPanel that will be used to retreive the dragged files
     * based on the current file selection.
     *
     * @param folderPanel the FolderPanel used to retrieve the list of selected/marked file(s) that are dragged
     */
    public FileDragSourceListener(FolderPanel folderPanel) {
        this.folderPanel = folderPanel;
    }


    /**
     * Enables drag operations on the specified component. This class will be notified wheneven drag operations
     * are performed on the component.
     *
     * @param c the component for which to add 'drag' support
     */
    public void enableDrag(Component c) {
        DragSource dragSource = DragSource.getDefaultDragSource();
        dragSource.createDefaultDragGestureRecognizer(c, DnDConstants.ACTION_COPY|DnDConstants.ACTION_MOVE|DnDConstants.ACTION_LINK, this);
    }


//    /**
//     * Creates a custom DragGestureEvent instance re-using the information contained in the given DragGestureEvent, but
//     * overridding the actions with the specified actions bitwise mask.
//     * When used with <code>DragSource.startDrag</code>, this allows to start a drag operation with a different source
//     * action set from the one specified in the <code>DragGestureRecognizer</code>, based on the current state and
//     * contents of the FolderPanel.
//     */
//    private DragGestureEvent createCustomDragGestureEvent(DragGestureEvent originalDGE, int actions) {
//        Vector eventList = new Vector();
//        Iterator eventIterator = originalDGE.iterator();
//
//        while(eventIterator.hasNext())
//            eventList.add(eventIterator.next());
//
//        DragGestureRecognizer dragGestureRecognizer = originalDGE.getSourceAsDragGestureRecognizer();
//        dragGestureRecognizer.setSourceActions(actions);
//
//        return new DragGestureEvent(dragGestureRecognizer,
//                actions,
//                originalDGE.getDragOrigin(),
//                eventList);
//    }


    /////////////////////////////////
    // DragGestureListener methods //
    /////////////////////////////////

    public void dragGestureRecognized(DragGestureEvent event) {
        if(folderPanel.getMainFrame().getNoEventsMode())
            return;

        FileTable fileTable = folderPanel.getFileTable();
        FileTableModel tableModel = fileTable.getFileTableModel();

        // Return (do not initiate drag) if mouse button2 or button3 was used
        if((event.getTriggerEvent().getModifiers() & (InputEvent.BUTTON2_MASK|InputEvent.BUTTON3_MASK))!=0)
            return;

// Do not use that to retrieve the current selected file as it is inaccurate: the selection could have changed since the
// the mouse was clicked.         
//        AbstractFile selectedFile = fileTable.getSelectedFile(false);
//        // Return if selected file is null (could happen if '..' is selected)
//        if(selectedFile==null)
//            return;

        // Find out which row was clicked
        int clickedRow = fileTable.rowAtPoint(event.getDragOrigin());
        // Return (do not initiate drag) if the selected file is the parent folder '..'
        if (clickedRow==-1 || fileTable.isParentFolder(clickedRow))
            return;

        // Retrieve the file corresponding to the clicked row
        AbstractFile selectedFile = tableModel.getFileAtRow(clickedRow);

        // Find out which files are to be dragged, based on the selected file and currenlty marked files.
        // If there are some files marked, drag marked files only if the selected file is one of the marked files.
        // In any other case, only drag the selected file.
        FileSet markedFiles;
        FileSet draggedFiles;
        if(tableModel.getNbMarkedFiles()>0 && (markedFiles=fileTable.getSelectedFiles()).contains(selectedFile)) {
            draggedFiles = markedFiles;
        }
        else {
            draggedFiles = new FileSet(folderPanel.getCurrentFolder(), selectedFile);
        }

        // Set initial DnDContext information
        DnDContext.setDragInitiatedByMucommander(true);
        DnDContext.setDragInitiator(folderPanel);
        DnDContext.setDragGestureModifiersEx(event.getTriggerEvent().getModifiersEx());

        // Start dragging
        DragSource.getDefaultDragSource().startDrag(event, null, new TransferableFileSet(draggedFiles), this);
//        DragSource.getDefaultDragSource().startDrag(createCustomDragGestureEvent(event, DnDConstants.ACTION_MOVE), null, new TransferableFileSet(draggedFiles), this);
    }


    ///////////////////////////////////////
    // DragSourceListener implementation //
    ///////////////////////////////////////

    public void dragEnter(DragSourceDragEvent event) {
        // Update drag gesture modifiers
        DnDContext.setDragGestureModifiersEx(event.getGestureModifiersEx());
    }

    public void dragOver(DragSourceDragEvent event) {
    }

    public void dropActionChanged(DragSourceDragEvent event) {
        // Update drag gesture modifiers
        DnDContext.setDragGestureModifiersEx(event.getGestureModifiersEx());
    }

    public void dragExit(DragSourceEvent event) {
    }

    public void dragDropEnd(DragSourceDropEvent event) {
        // Reset DnDContext information
        DnDContext.setDragInitiatedByMucommander(false);
        DnDContext.setDragInitiator(null);
        DnDContext.setDragGestureModifiersEx(0);
    }
}
