/**
 *
 */
package com.github.apixandru.games.rummikub.ui;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * @author Alexandru-Constantin Bledea
 * @since Oct 13, 2015
 */
final class CardDndListener extends MouseAdapter {

    private final Color hoverColor = Color.PINK;

    private final DragSource dragSource;

    private CardSlot draggablePieceParent;
    private CardUi draggablePiece;

    private Container dropTarget;
    private Color dropTargetOriginalColor;


    private int xOffset;
    private int yOffset;


    /**
     * @param dragSource
     */
    CardDndListener(final DragSource dragSource) {
        this.dragSource = dragSource;
    }

    /* (non-Javadoc)
     * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
     */
    @Override
    public void mousePressed(final MouseEvent e) {
        final CardUi card = getCard(e);
        if (null == card) {
            return;
        }
        this.draggablePiece = card;

        this.draggablePieceParent = (CardSlot) card.getParent();

        computeHoverOffset(e);

        this.dragSource.beginDrag(this.draggablePiece);

        updateMovingPieceLocation(e);
    }

    /**
     * @param e
     */
    private void computeHoverOffset(MouseEvent e) {
        final Point parentLocation = this.dragSource.getPosition(this.draggablePiece);

        this.xOffset = parentLocation.x - e.getX();
        this.yOffset = parentLocation.y - e.getY();
    }

    /* (non-Javadoc)
     * @see java.awt.event.MouseAdapter#mouseDragged(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseDragged(final MouseEvent e) {
        if (this.draggablePiece == null) {
            return;
        }
        updateMovingPieceLocation(e);
        updateDropIndicator(e);
    }

    /**
     * @param event
     */
    private void updateDropIndicator(final MouseEvent event) {
        final Container component = getComponentOrInitialLocation(event);

        if (this.dropTarget != component) {
            UiUtil.setBackground(this.dropTarget, this.dropTargetOriginalColor);

            this.dropTargetOriginalColor = UiUtil.getBackground(component);
            UiUtil.setBackground(component, this.hoverColor);
            this.dropTarget = component;
        }
    }

    /* (non-Javadoc)
     * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseReleased(final MouseEvent e) {
        if (this.draggablePiece == null) {
            return;
        }
        this.dragSource.endDrag(this.draggablePiece);

        final Container destination = getComponentOrInitialLocation(e);
        final BoardUi board = getBoard(destination);
        if (null != board) {
            board.placeCard(this.draggablePiece, (CardSlot) destination);
        } else {
            destination.add(this.draggablePiece);
            destination.validate();
        }
        this.draggablePiece = null;
    }

    private BoardUi getBoard(final Container container) {
        Component component = container;
        while (component != null && !(component instanceof BoardUi)) {
            component = component.getParent();
        }
        return (BoardUi) component;
    }

    /**
     * @param event
     * @return
     */
    private Container getComponentOrInitialLocation(final MouseEvent event) {
        final JComponent component = getComponentAt(event);
        if (null == component || !canDrop(component)) {
            return this.draggablePieceParent;
        }
        return component;
    }

    /**
     * @param component
     * @return
     */
    private static boolean canDrop(JComponent component) {
        // TODO fix this
        if (component instanceof CardSlot) {
            return true;
        }
        return false;
    }

    /**
     * @param event
     * @return
     */
    private CardUi getCard(final MouseEvent event) {
        Component c = getComponentAt(event);
        while (null != c && !(c instanceof CardUi)) {
            c = c.getParent();
        }
        return (CardUi) c;
    }

    /**
     * @param event
     * @return
     */
    private JComponent getComponentAt(final MouseEvent event) {
        return this.dragSource.getComponentAt(event.getX(), event.getY());
    }

    /**
     * @param event
     */
    private void updateMovingPieceLocation(final MouseEvent event) {
        this.draggablePiece.setLocation(event.getX() + xOffset, event.getY() + yOffset);
    }

}

@@ -2670,23 +2670,25 @@
 ner dest
-ination
+Component
  = getCo
@@ -2728,39 +2728,89 @@
     
-final BoardUi board = getBoard(
+JGridPanel destination = find(destComponent, BoardUi.class);%0A        if (null == 
 dest
@@ -2809,33 +2809,34 @@
  == destination)
-;
+ %7B
 %0A        if (nul
@@ -2832,28 +2832,62 @@
     
-if (null != board) %7B
+    destination = find(destComponent, PlayerUi.class);
 %0A   
@@ -2895,17 +2895,29 @@
     
+%7D%0A
     
-board
+    destination
 .pla
@@ -2959,23 +2959,25 @@
 ot) dest
-ination
+Component
 );%0A     
@@ -2983,45 +2983,8 @@
     
-%7D else %7B%0A            destination.add(
 this
@@ -3002,99 +3002,116 @@
 iece
-)
+ = null
 ;%0A    
+%7D%0A%0A
     
-    destination.validate();%0A        %7D%0A        this.draggablePiece = null;
+/**%0A     * @param container%0A     * @param clasz%0A     * @param %3CT%3E%0A     * @return
 %0A    
-%7D%0A
+ */
 %0A   
@@ -3123,23 +3123,36 @@
 ate 
-BoardUi getBoar
+%3CT extends JGridPanel%3E T fin
 d(fi
@@ -3174,16 +3174,32 @@
 ontainer
+, Class%3CT%3E clasz
 ) %7B%0A    
@@ -3276,37 +3276,34 @@
 && !
-(component instanceof BoardUi
+clasz.isInstance(component
 )) %7B
@@ -3376,23 +3376,17 @@
 return (
-BoardUi
+T
 ) compon

use jgridpanel