package CH.ifa.draw.application;

import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import CH.ifa.draw.DrawPlugin;
import CH.ifa.draw.io.ImageFileFilter;

/**
 * A DropTargetListener that only reacts on dropped files. The files are
 * filtered by a given file filter. The file filter can be changed in a
 * subclass. The handling of the files has to be implemented in a subclass.
 *
 * @author 6hauster
 */
public abstract class AbstractFileDragDropListener implements DropTargetListener {
    private static final org.apache.log4j.Logger logger =
        org.apache.log4j.Logger.getLogger(AbstractFileDragDropListener.class);

    /**
     * Get the file filter which is used to filter the dropped files.
     *
     * @return the file filter
     */
    protected FileFilter getFileFilter() {
        return Objects.requireNonNull(DrawPlugin.getCurrent()).getIOHelper()
            .getKnownFileFiltersWithImportFormats();
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
     */
    @Override
    public void drop(DropTargetDropEvent event) {
        // Accept copy drops
        event.acceptDrop(DnDConstants.ACTION_COPY);

        // Get the transfer which can provide the dropped item data
        Transferable transferable = event.getTransferable();

        // Get the data formats of the dropped item
        DataFlavor[] flavors = transferable.getTransferDataFlavors();

        // Loop through the flavors
        for (DataFlavor flavor : flavors) {
            try {
                // If the drop items are files
                if (flavor.isFlavorJavaFileListType()) {
                    // Get all the dropped files
                    @SuppressWarnings("unchecked")
                    List<File> files = (List<File>) transferable.getTransferData(flavor);


                    // filter the files
                    List<File> filteredFilesDrawings = new ArrayList<>();
                    FileFilter fileFilterDrawings = getFileFilter();
                    List<File> filteredFilesImages = new ArrayList<>();
                    FileFilter fileFilterImages = new ImageFileFilter();

                    for (File file : files) {
                        if (fileFilterDrawings.accept(file)) {
                            filteredFilesDrawings.add(file);
                        } else if (fileFilterImages.accept(file)) {
                            filteredFilesImages.add(file);
                        }
                    }

                    // handle the filtered files at once
                    File[] filesArrayDrawings = filteredFilesDrawings.toArray(new File[0]);
                    if (filesArrayDrawings.length > 0) {
                        handleFilesDrawing(filesArrayDrawings, event.getLocation());
                    }
                    File[] filesArrayImages = filteredFilesImages.toArray(new File[0]);
                    if (filesArrayImages.length > 0) {
                        handleFilesImage(filesArrayImages, event.getLocation());
                    }
                }
            } catch (IOException | UnsupportedFlavorException e) {
                // Print out the error stack
                logger.error(e.getMessage());
                if (logger.isDebugEnabled()) {
                    logger.debug(AbstractFileDragDropListener.class.getSimpleName() + ": " + e);
                }
            }
        }

        // Inform that the drop is complete
        event.dropComplete(true);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent
     * )
     */
    @Override
    public void dragEnter(DropTargetDragEvent event) {}

    /*
     * (non-Javadoc)
     *
     * @see
     * java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
     */
    @Override
    public void dragExit(DropTargetEvent event) {}

    /*
     * (non-Javadoc)
     *
     * @see
     * java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent
     * )
     */
    @Override
    public void dragOver(DropTargetDragEvent event) {}

    /*
     * (non-Javadoc)
     *
     * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.
     * DropTargetDragEvent)
     */
    @Override
    public void dropActionChanged(DropTargetDragEvent event) {}

    /**
     * Handle the dropped Drawings.
     *
     * @param files the files as array.
     * @param loc   where the files are dropped.
     */
    protected abstract void handleFilesDrawing(File[] files, Point loc);

    /**
     * Handle the dropped Images.
     *
     * @param files the files as array.
     * @param loc   where the files are dropped.
     */
    protected abstract void handleFilesImage(File[] files, Point loc);
}