/*
 * Copyright (c) 2012, Oracle  and/or its affiliates. All rights reserved.
 *
 * Use is subject to license terms.
 */
package com.sun.javafx.tk.quantum;

import javafx.application.Platform;

import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

import com.sun.glass.ui.Clipboard;
import com.sun.glass.ui.ClipboardAssistance;

public class GlassSceneDnDEventHandler {

    private final GlassScene scene;
    private final boolean verbose;
    private final DragEventListener del;
    
    public static interface DragEventListener {
        void eventAccepted(TransferMode tm);
        void eventRejected();
    }

    public GlassSceneDnDEventHandler(final GlassScene scene,
                                     final DragEventListener del,
                                     final boolean verbose) {
        this.scene = scene;
        this.verbose = verbose;
        this.del = del;
    }

    public GlassSceneDnDEventHandler(final GlassScene scene,
                                     final DragEventListener del) {
        this(scene, del, false);
    }
    private final static QuantumToolkit toolkit =
            (QuantumToolkit) QuantumToolkit.getToolkit();

    private static boolean toolkit() {
        return (toolkit.getFxUserThread() != null);
    }

    public TransferMode handleDragEnter(int x, int y, int xAbs, int yAbs,
                                        int recommendedDropAction,
                                        ClipboardAssistance dropTargetAssistant) {
        assert Platform.isFxApplicationThread();
        TransferMode action = null;

        try {
            if (scene.dropTargetListener != null && toolkit()) {
                final Dragboard dragboard = createDragboard(dropTargetAssistant);
                final GlassDragEvent event = createEvent(GlassDragEvent.ENTER, x,
                                                         y, xAbs, yAbs,
                                                         dragboard, 0, scene,
                                                         recommendedDropAction,
                                                         del);
                action = scene.dropTargetListener.dragEnter(event);
            }
        } catch (RuntimeException ex) {
            if (verbose) {
                ex.printStackTrace();
            }
            throw ex;
        } catch (Throwable t) {
            if (verbose) {
                t.printStackTrace();
            }
            throw new RuntimeException(t);
        }
        return action;
    }

    /*
     * GlassScene -> QuantumToolkit -> PrismEventUtils -> TK normalized
     * DragEvent
     */
    public void handleDragLeave(ClipboardAssistance dropTargetAssistant) {
        assert Platform.isFxApplicationThread();
        try {
            if (scene.dropTargetListener != null && toolkit()) {
                final Dragboard dragboard = createDragboard(dropTargetAssistant);
                final GlassDragEvent event = createEvent(GlassDragEvent.LEAVE, 0,
                                                         0, 0, 0, dragboard, 0,
                                                         scene,
                                                         Clipboard.ACTION_NONE,
                                                         del);
                scene.dropTargetListener.dragExit(event);
            }
        } catch (RuntimeException ex) {
            if (verbose) {
                ex.printStackTrace();
            }
            throw ex;
        } catch (Throwable t) {
            if (verbose) {
                t.printStackTrace();
            }
            throw new RuntimeException(t);
        }
    }

    public TransferMode handleDragDrop(int x, int y, int xAbs, int yAbs,
                                       int recommendedDropAction,
                                       ClipboardAssistance dropTargetAssistant) {
        assert Platform.isFxApplicationThread();
        TransferMode action = null;
        try {
            final Dragboard dragboard = createDragboard(dropTargetAssistant);
            final GlassDragEvent event = createEvent(GlassDragEvent.END, x, y,
                                                     xAbs, yAbs, dragboard, 0,
                                                     scene,
                                                     recommendedDropAction, del);
            if (scene.dropTargetListener != null && toolkit()) {
                action = scene.dropTargetListener.drop(event);
            }
        } catch (RuntimeException ex) {
            if (verbose) {
                ex.printStackTrace();
            }
            throw ex;
        } catch (Throwable t) {
            if (verbose) {
                t.printStackTrace();
            }
            throw new RuntimeException(t);
        }
        return action;
    }

    public TransferMode handleDragOver(int x, int y, int xAbs, int yAbs,
                                       int recommendedDropAction,
                                       ClipboardAssistance dropTargetAssistant) {
        assert Platform.isFxApplicationThread();
        TransferMode action = null;

        try {
            if (scene.dropTargetListener != null && toolkit()) {
                final Dragboard dragboard = createDragboard(dropTargetAssistant);
                final GlassDragEvent event = createEvent(GlassDragEvent.OVER, x,
                                                         y, xAbs, yAbs,
                                                         dragboard, 0, scene,
                                                         recommendedDropAction,
                                                         del);
                action = scene.dropTargetListener.dragOver(event);
            }
        } catch (RuntimeException ex) {
            if (verbose) {
                ex.printStackTrace();
            }
            throw ex;
        } catch (Throwable t) {
            if (verbose) {
                t.printStackTrace();
            }
            throw new RuntimeException(t);
        }
        return action;
    }

    public void handleDragStart(int button, int x, int y, int xAbs, int yAbs,
                                ClipboardAssistance dropSourceAssistant) {
        assert Platform.isFxApplicationThread();
        try {
            if (scene.dragGestureListener != null && toolkit()) {
                final Dragboard dragboard = createDragboard(dropSourceAssistant);
                final GlassDragEvent de =
                        createEvent(GlassDragEvent.START, x, y, xAbs, yAbs,
                                    dragboard, button, scene,
                                    Clipboard.ACTION_NONE, del);
                scene.dragGestureListener.dragGestureRecognized(de);
            }
        } catch (RuntimeException ex) {
            if (verbose) {
                ex.printStackTrace();
            }
            throw ex;
        } catch (Throwable t) {
            if (verbose) {
                t.printStackTrace();
            }
            throw new RuntimeException(t);
        }
    }

    // Used in case the drag has started from the handleDragStart() above - 
    // it's delivered by Glass itself.
    // Otherwise, see QuantumToolkit.createDragboard() and startDrag()
    public void handleDragEnd(int performedAction,
                              ClipboardAssistance dropSourceAssistant) {
        assert Platform.isFxApplicationThread();
        if (scene.dragSourceListener != null && toolkit()) {
            final Dragboard dragboard = createDragboard(dropSourceAssistant);
            final GlassDragEvent event = createEvent(GlassDragEvent.END, 0, 0, 0,
                                                     0, dragboard, 0, scene,
                                                     performedAction, del);
            scene.dragSourceListener.dragDropEnd(event);
        }
    }

    public TransferMode handleDragEnter(int x, int y, int xAbs, int yAbs,
                                        TransferMode recommendedDropAction,
                                        ClipboardAssistance dropTargetAssistant) {
        return handleDragEnter(x, y, xAbs, yAbs, TransferModeToAction(
                recommendedDropAction), dropTargetAssistant);
    }

    public TransferMode handleDragDrop(int x, int y, int xAbs, int yAbs,
                                       TransferMode recommendedDropAction,
                                       ClipboardAssistance dropTargetAssistant) {
        return handleDragDrop(x, y, xAbs, yAbs, TransferModeToAction(
                recommendedDropAction), dropTargetAssistant);
    }

    public TransferMode handleDragOver(int x, int y, int xAbs, int yAbs,
                                       TransferMode recommendedDropAction,
                                       ClipboardAssistance dropTargetAssistant) {
        return handleDragOver(x, y, xAbs, yAbs, TransferModeToAction(
                recommendedDropAction), dropTargetAssistant);
    }

    public void handleDragEnd(TransferMode performedAction,
                              ClipboardAssistance dropSourceAssistant) {
        handleDragEnd(TransferModeToAction(performedAction), dropSourceAssistant);
    }

    public static int TransferModeToAction(TransferMode tm) {
        if (tm == null) {
            return Clipboard.ACTION_NONE;
        }

        switch (tm) {
            case COPY:
                return Clipboard.ACTION_COPY;
            case MOVE:
                return Clipboard.ACTION_MOVE;
            case LINK:
                return Clipboard.ACTION_REFERENCE;
            default:
                return Clipboard.ACTION_NONE;
        }
    }

    public static Dragboard createDragboard(ClipboardAssistance assistant) {
        final QuantumClipboard board = QuantumClipboard.getDragboardInstance(
                assistant);
        return Dragboard.impl_create(board);
    }
    
    private static GlassDragEvent createEvent(int type, int x, int y, int xAbs,
                                              int yAbs, Dragboard db, int button,
                                              GlassScene glassScene,
                                              int recommendedDropAction,
                                              DragEventListener del) {
        if (del == null) {
            return new GlassDragEvent(type, x, y, xAbs, yAbs, db, button,
                                      glassScene, recommendedDropAction);
        }
        return new GlassDragEventWithListener(type, x, y, xAbs, yAbs, db, button,
                                              glassScene, recommendedDropAction,
                                              del);
    }

    static private class GlassDragEventWithListener extends GlassDragEvent {

        private final DragEventListener del;

        GlassDragEventWithListener(int type, int x, int y, int xAbs, int yAbs,
                                   Dragboard db, int button,
                                   GlassScene glassScene,
                                   int recommendedDropAction,
                                   DragEventListener del) {
            super(type, x, y, xAbs, yAbs, db, button, glassScene,
                  recommendedDropAction);
            this.del = del;
        }

        @Override
        public void accept(TransferMode tm) {
            del.eventAccepted(tm);
        }

        @Override
        public void reject() {
            del.eventRejected();
        }
    }
}
