package net.sf.jabref.gui;

import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

/**
 * Extends the JTabbedPane class to support Drag&Drop of Tabs.
 *
 * @author kleinms, strassfn
 */
class DragDropPane extends JTabbedPane {

    private boolean draggingState; // State var if we are at dragging or not
    private int indexDraggedTab; // The index of the tab we drag at the moment
    private final MarkerPane markerPane; // The glass panel for painting the position marker


    DragDropPane() {
        super();
        indexDraggedTab = -1;
        markerPane = new MarkerPane();
        markerPane.setVisible(false);

        // -------------------------------------------
        // Adding listeners for Drag&Drop Actions
        // -------------------------------------------
        addMouseMotionListener(new MouseMotionAdapter() {

            @Override
            public void mouseDragged(MouseEvent e) { // Mouse is dragging
                // Calculates the tab index based on the mouse position
                int indexActTab = getUI().tabForCoordinate(DragDropPane.this,
                        e.getX(), e.getY());
                if (draggingState) { // We are at tab dragging
                    if ((indexDraggedTab >= 0) && (indexActTab >= 0)) { //Is it a valid scenario?
                        boolean toTheLeft = e.getX() <= getUI().getTabBounds(DragDropPane.this, indexActTab).getCenterX(); //Go to the left or to the right of the actual Tab
                        DragDropPane.this.getRootPane().setGlassPane(markerPane); //Set the MarkerPane as glass Pane
                        Rectangle actTabRect = SwingUtilities.convertRectangle(DragDropPane.this, getBoundsAt(indexActTab),
                                DragDropPane.this.markerPane); //Rectangle with the same dimensions as the tab at the mouse position
                        if (toTheLeft) {
                            markerPane.setPicLocation(new Point(actTabRect.x, actTabRect.y
                                    + actTabRect.height)); //Set pic to the left of the tab at the mouse position
                        }
                        else {
                            markerPane.setPicLocation(new Point(actTabRect.x + actTabRect.width, actTabRect.y
                                    + actTabRect.height)); //Set pic to the right of the tab at the mouse position
                        }

                        markerPane.setVisible(true);
                        markerPane.repaint();
                        repaint();
                    } else { //We have no valid tab tragging scenario
                        markerPane.setVisible(false);
                        markerPane.repaint();
                    }

                } else { //We are not at tab dragging
                    if (indexActTab >= 0) { // Mouse is above a tab, otherwise tabNumber would be -1
                        // -->Now we are at tab tragging
                        draggingState = true; // Mark now we are at dragging
                        indexDraggedTab = indexActTab; // Set draggedTabIndex to the tabNumber where we are now
                        repaint();
                    }
                }
                super.mouseDragged(e);
            }
        });

        addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                DragDropPane.this.markerPane.setVisible(false); //Set MarkerPane invisible
                int indexActTab = getUI().tabForCoordinate(DragDropPane.this,
                        e.getX(), e.getY());
                if ((indexDraggedTab >= 0) && (indexActTab >= 0) && (indexDraggedTab != indexActTab)) { //Is it a valid scenario?
                    if (draggingState) { //We are at tab dragging
                        boolean toTheLeft = e.getX() <= getUI().getTabBounds(DragDropPane.this, indexActTab).getCenterX(); //Go to the left or to the right of the actual Tab
                        DragDropPane.this.markerPane.setVisible(false);

                        Component actTab = getComponentAt(indexDraggedTab); //Save dragged tab
                        String actTabTitle = getTitleAt(indexDraggedTab); //Save Title of the dragged tab
                        removeTabAt(indexDraggedTab); //Remove dragged tab
                        int newTabPos;
                        if (indexActTab < indexDraggedTab) { //We are dragging the tab to the left of its the position
                            if (toTheLeft && (indexActTab < (DragDropPane.this.getTabCount()))) {
                                newTabPos = indexActTab;
                            } else {
                                newTabPos = indexActTab + 1;
                            }
                        } else { //We are dragging the tab to the right of the old position
                            if (toTheLeft && (indexActTab > 0)) {
                                newTabPos = indexActTab - 1;
                            } else {
                                newTabPos = indexActTab;
                            }
                        }
                        insertTab(actTabTitle, null, actTab, null, newTabPos); //Insert dragged tab at new position
                        DragDropPane.this.setSelectedIndex(newTabPos); //Set selection back to the tab (at the new tab position
                    }
                }
                draggingState = false;
            }
        });
    }


    /**
     * A glass panel which sets the marker for Dragging of Tabs.
     *
     */
    static class MarkerPane extends JPanel {

        private Point locationP;
        private final IconTheme.JabRefIcon moveTabArrow;


        public MarkerPane() {
            setOpaque(false);

            // Sets the marker fontIcon
            moveTabArrow = IconTheme.JabRefIcon.MOVE_TAB_ARROW;
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setComposite(AlphaComposite.getInstance(
                    AlphaComposite.SRC_OVER, 0.9f)); // Set transparency
            g.setFont(IconTheme.FONT.deriveFont(Font.BOLD, 24f));
            g.drawString(moveTabArrow.getCode(), locationP.x - (moveTabArrow.getIcon().getIconWidth() / 2),
                    locationP.y + (moveTabArrow.getIcon().getIconHeight() / 2));

        }

        /**
         * Sets the new location, where the marker should be placed.
         *
         * @param pt the point for the marker
         */
        public void setPicLocation(Point pt) {
            this.locationP = pt;
        }

    }
}