/*
 * Copyright (c) 2007-2011 by The Broad Institute of MIT and Harvard.  All Rights Reserved.
 *
 * This software is licensed under the terms of the GNU Lesser General Public License (LGPL),
 * Version 2.1 which is available at http://www.opensource.org/licenses/lgpl-2.1.php.
 *
 * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR
 * WARRANTES OF ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING,
 * WITHOUT LIMITATION, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, WHETHER
 * OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR RESPECTIVE
 * TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES
 * OF ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
 * ECONOMIC DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER
 * THE BROAD OR MIT SHALL BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT
 * SHALL KNOW OF THE POSSIBILITY OF THE FOREGOING.
 */

/*
 * TrackPanel.java
 *
 * Created on Sep 5, 2007, 4:09:39 PM
 *
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.broad.igv.ui.panel;


import org.broad.igv.feature.RegionOfInterest;
import org.broad.igv.track.TrackMenuUtils;
import org.broad.igv.ui.IGV;
import org.broad.igv.ui.util.SwitchingLabelUI;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;


/**
 * The drag & drop code was modified from the excellent example of Bryan E. Smith.
 *
 * @author jrobinso
 */
public class HeaderPanel extends JPanel implements Transferable {

    ReferenceFrame frame;
    private JLabel label;
    public static final Color BUTTON_BACKGROUND = new Color(230, 240, 250);
    static DataFlavor dragAndDropPanelDataFlavor;
    private CytobandPanel cytobandPanel;
    private RulerPanel rulerPanel;
    private RegionOfInterestPanel regionOfInterestPane;
    private JPanel geneListPanel;


    public HeaderPanel(ReferenceFrame frame) {
        this.frame = frame;
        init();
    }

    private void init() {


        setBackground(new java.awt.Color(255, 255, 255));
        setMinimumSize(new java.awt.Dimension(700, 0));
        setPreferredSize(new java.awt.Dimension(0, 0));
        setLayout(new java.awt.BorderLayout());

        if (FrameManager.isGeneListMode()) {

            geneListPanel = new JPanel();
            geneListPanel.setMinimumSize(new java.awt.Dimension(700, 0));
            geneListPanel.setPreferredSize(new java.awt.Dimension(0, 0));
            geneListPanel.setLayout(new java.awt.BorderLayout());


            label = new JLabel(frame.getName());
            label.setForeground(Color.blue);
            label.setUI(new SwitchingLabelUI(10));
            label.setToolTipText(frame.getName());
            label.setPreferredSize(new Dimension(500, 80));

            //label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            //label.addMouseListener(mouseAdapter);


            setSize(400, 100);
            setVisible(true);

            cytobandPanel = new CytobandPanel(frame, false);
            cytobandPanel.setBackground(new java.awt.Color(255, 255, 255));
            cytobandPanel.setPreferredSize(new java.awt.Dimension(0, 20));
            cytobandPanel.setRequestFocusEnabled(false);
            cytobandPanel.setLayout(null);

            final MouseAdapter mouseAdapter = new MouseAdapter() {

                boolean isDragging = false;
                Point mousePressPoint;

                @Override
                public void mouseClicked(MouseEvent mouseEvent) {
                    if (mouseEvent.getClickCount() > 1) {
                        IGV.getInstance().setDefaultFrame(frame.getName());
                    }
                }


                public void mousePressed(MouseEvent evt) {
                    if (evt.isPopupTrigger()) {
                        getPopupMenu(HeaderPanel.this, frame).show(HeaderPanel.this, evt.getX(), evt.getY());
                    }
                }

                @Override
                public void mouseReleased(MouseEvent evt) {

                    if (evt.isPopupTrigger()) {
                        getPopupMenu(HeaderPanel.this, frame).show(HeaderPanel.this, evt.getX(), evt.getY());
                    }
                    isDragging = false;

                }


                @Override()
                public void mouseDragged(MouseEvent e) {

                    if (isDragging) {
                        return;
                    }

                    isDragging = true;
                    JComponent c = HeaderPanel.this;
                    TransferHandler handler = c.getTransferHandler();
                    if (handler != null) {
                        handler.exportAsDrag(c, e, TransferHandler.MOVE);
                    }

                }
            };

            cytobandPanel.addMouseListener(mouseAdapter);
            cytobandPanel.addMouseMotionListener(mouseAdapter);

            label.addMouseListener(mouseAdapter);
            label.addMouseMotionListener(mouseAdapter);

            this.addMouseListener(mouseAdapter);
            this.addMouseMotionListener(mouseAdapter);

            geneListPanel.add(cytobandPanel, BorderLayout.CENTER);
            geneListPanel.add(label, BorderLayout.SOUTH);
            add(geneListPanel);

            this.setTransferHandler(new DragAndDropTransferHandler());
            // Create the listener to do the work when dropping on this object!
            this.setDropTarget(new DropTarget(this, new HeaderDropTargetListener(this)));


        } else {

            JPanel panel = new JPanel();
            setBorder(javax.swing.BorderFactory.createLineBorder(Color.gray));
            panel.setBackground(new java.awt.Color(255, 255, 255));
            panel.setMinimumSize(new java.awt.Dimension(700, 0));
            panel.setPreferredSize(new java.awt.Dimension(0, 0));
            panel.setLayout(new java.awt.BorderLayout());

            cytobandPanel = new CytobandPanel(frame);
            cytobandPanel.setBackground(new java.awt.Color(255, 255, 255));
            cytobandPanel.setPreferredSize(new java.awt.Dimension(0, 50));
            cytobandPanel.setRequestFocusEnabled(false);
            cytobandPanel.setLayout(null);
            panel.add(cytobandPanel, java.awt.BorderLayout.NORTH);

            rulerPanel = new RulerPanel(frame);
            rulerPanel.setBackground(new java.awt.Color(255, 255, 255));
            rulerPanel.setLayout(null);
            panel.add(rulerPanel, java.awt.BorderLayout.CENTER);

            regionOfInterestPane = new RegionOfInterestPanel(frame);
            regionOfInterestPane.setBackground(new java.awt.Color(255, 255, 255));
            regionOfInterestPane.setMinimumSize(new java.awt.Dimension(0, 13));


            panel.add(regionOfInterestPane, java.awt.BorderLayout.SOUTH);

            add(panel);
        }


    }


    // TODO -- this is a partial copy of the RegionOfInterestPanel method.  Refactor to share


    protected JPopupMenu getPopupMenu(final HeaderPanel parent, final ReferenceFrame frame) {

        int start = (int) frame.getOrigin();
        int end = (int) frame.getEnd();
        final RegionOfInterest roi = new RegionOfInterest(frame.getChrName(), start, end, "");

        JPopupMenu popupMenu = new RegionMenu(roi, frame, "Panel: " + frame.getName());


        // Zoom items
        popupMenu.addSeparator();
        TrackMenuUtils.addZoomItems(popupMenu, frame);
        popupMenu.addSeparator();
        JMenuItem item1 = new JMenuItem("Switch to standard view");
        item1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                IGV.getInstance().setDefaultFrame(frame.getName());

            }
        });
        popupMenu.add(item1);

        popupMenu.addSeparator();
        JMenuItem item = new JMenuItem("Remove panel");
        item.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                parent.removeFrame(frame);
            }
        });
        popupMenu.add(item);


        return popupMenu;
    }

    private void removeFrame(ReferenceFrame frame) {
        FrameManager.removeFrame(frame);
        java.util.List<ReferenceFrame> remainingFrames = FrameManager.getFrames();
        if (remainingFrames.size() == 1) {
            IGV.getInstance().setDefaultFrame(remainingFrames.get(0).getName());
        } else {
            IGV.getInstance().resetFrames();
        }
    }

    @Override

    public void setBackground(Color color) {
        super.setBackground(color);
        if (cytobandPanel != null) cytobandPanel.setBackground(color);
        if (rulerPanel != null) rulerPanel.setBackground(color);
        if (regionOfInterestPane != null) regionOfInterestPane.setBackground(color);
        if (geneListPanel != null) geneListPanel.setBackground(color);
    }


    /**
     * <p>Returns (creating, if necessary) the DataFlavor representing RandomDragAndDropPanel</p>
     *
     * @return
     */
    public static DataFlavor getDragAndDropPanelDataFlavor() throws Exception {
        // Lazy load/create the flavor
        if (dragAndDropPanelDataFlavor == null) {
            dragAndDropPanelDataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
                    ";class=org.broad.igv.ui.panel.HeaderPanel");
        }

        return dragAndDropPanelDataFlavor;
    }

    //private static final Cursor droppableCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
    //private static final Cursor notDroppableCursor = Cursor.getDefaultCursor();

    /**
     * <p>Listens for drops and performs the updates.</p>
     * <p>The real magic behind the drop!</p>
     */
    class HeaderDropTargetListener implements DropTargetListener {

        private final HeaderPanel rootPanel;

        /**
         * <p>Two cursors with which we are primarily interested while dragging:</p>
         * <ul>
         * <li>Cursor for droppable condition</li>
         * <li>Cursor for non-droppable consition</li>
         * </ul>
         * <p>After drop, we manually change the cursor back to default, though does this anyhow -- just to be complete.</p>
         */

        public HeaderDropTargetListener(HeaderPanel sheet) {
            this.rootPanel = sheet;
        }

        // Could easily find uses for these, like cursor changes, etc.

        public void dragEnter(DropTargetDragEvent dtde) {
        }

        public void dragOver(DropTargetDragEvent dtde) {
            // if (!this.rootPanel.getCursor().equals(droppableCursor)) {
            //     this.rootPanel.setCursor(droppableCursor);
            // }
        }

        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        public void dragExit(DropTargetEvent dte) {
            // this.rootPanel.setCursor(notDroppableCursor);
        }

        /**
         * <p>The user drops the item. Performs the drag and drop calculations and layout.</p>
         *
         * @param dtde
         */
        public void drop(DropTargetDropEvent dtde) {

            // Done with cursors, dropping
            //this.rootPanel.setCursor(Cursor.getDefaultCursor());

            // Just going to grab the expected DataFlavor to make sure
            // we know what is being dropped
            DataFlavor dragAndDropPanelFlavor = null;
            Object transferableObj = null;

            try {
                // Grab expected flavor
                dragAndDropPanelFlavor = HeaderPanel.getDragAndDropPanelDataFlavor();

                Transferable transferable = dtde.getTransferable();

                // What does the Transferable support
                if (transferable.isDataFlavorSupported(dragAndDropPanelFlavor)) {
                    transferableObj = dtde.getTransferable().getTransferData(dragAndDropPanelFlavor);
                }

            } catch (Exception ex) { /* nope, not the place */ }

            // If didn't find an item, bail
            if (transferableObj == null) {
                return;
            }

            // Cast it to the panel. By this point, we have verified it is a HeaderPanel
            HeaderPanel droppedPanel = (HeaderPanel) transferableObj;
            ReferenceFrame droppedFrame = droppedPanel.frame;
            if (droppedFrame == frame) {
                IGV.getInstance().resetFrames();
            } else {
                final int dropXLoc = dtde.getLocation().x;
                boolean before = dropXLoc < getWidth() / 2;


                // Find the index for the drop
                java.util.List<ReferenceFrame> panels = FrameManager.getFrames();
                java.util.List<ReferenceFrame> orderedPanels = new ArrayList(panels.size());
                panels.remove(droppedFrame);

                boolean dropAdded = false;


                for (ReferenceFrame frame : panels) {
                    if (HeaderPanel.this.frame == frame) {
                        if (before) {
                            orderedPanels.add(droppedFrame);
                            orderedPanels.add(frame);
                        } else {
                            orderedPanels.add(frame);
                            orderedPanels.add(droppedFrame);
                        }
                        dropAdded = true;
                    } else {
                        orderedPanels.add(frame);
                    }
                }


                // Request relayout contents, or else won't update GUI following drop.
                // Will add back in the order to which we just sorted
                FrameManager.setFrames(orderedPanels);
                IGV.getInstance().resetFrames();
            }
        }
    } // HeaderDropTargetListener

    /**
     * <p>One of three methods defined by the Transferable interface.</p>
     * <p>If multiple DataFlavor's are supported, can choose what Object to return.</p>
     * <p>In this case, we only support one: the actual JPanel.</p>
     * <p>Note we could easily support more than one. For example, if supports text and drops to a JTextField, could return the label's text or any arbitrary text.</p>
     *
     * @param flavor
     * @return
     */
    public Object getTransferData(DataFlavor flavor) {

        DataFlavor thisFlavor = null;

        try {
            thisFlavor = getDragAndDropPanelDataFlavor();
        } catch (Exception ex) {
            System.err.println("Problem lazy loading: " + ex.getMessage());
            ex.printStackTrace(System.err);
            return null;
        }

        // For now, assume wants this class... see loadDnD
        if (thisFlavor != null && flavor.equals(thisFlavor)) {
            return this;
        }

        return null;
    }

    /**
     * <p>One of three methods defined by the Transferable interface.</p>
     * <p>Returns supported DataFlavor. Again, we're only supporting this actual Object within the JVM.</p>
     * <p>For more information, see the JavaDoc for DataFlavor.</p>
     *
     * @return
     */
    public DataFlavor[] getTransferDataFlavors() {

        DataFlavor[] flavors = {null};
        try {
            flavors[0] = getDragAndDropPanelDataFlavor();
        } catch (Exception ex) {
            System.err.println("Problem lazy loading: " + ex.getMessage());
            ex.printStackTrace(System.err);
            return null;
        }

        return flavors;
    }

    /**
     * <p>One of three methods defined by the Transferable interface.</p>
     * <p>Determines whether this object supports the DataFlavor. In this case, only one is supported: for this object itself.</p>
     *
     * @param flavor
     * @return True if DataFlavor is supported, otherwise false.
     */
    public boolean isDataFlavorSupported(DataFlavor flavor) {

        DataFlavor[] flavors = {null};
        try {
            flavors[0] = getDragAndDropPanelDataFlavor();
        } catch (Exception ex) {
            System.err.println("Problem lazy loading: " + ex.getMessage());
            ex.printStackTrace(System.err);
            return false;
        }

        for (DataFlavor f : flavors) {
            if (f.equals(flavor)) {
                return true;
            }
        }

        return false;
    }

}

