/*
 * Copyright (c) 2007-2012 The Broad Institute, Inc.
 * SOFTWARE COPYRIGHT NOTICE
 * This software and its documentation are the copyright of the Broad Institute, Inc. All rights are reserved.
 *
 * This software is supplied without any warranty or guaranteed support whatsoever. The Broad Institute is not responsible for its use, misuse, or functionality.
 *
 * 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.
 */
/*
 * IGVCommandBar.java
 *
 * Created on April 5, 2008, 10:02 AM
 */
package org.broad.igv.ui;


import com.google.common.eventbus.Subscribe;
import com.jidesoft.hints.ListDataIntelliHints;
import com.jidesoft.swing.JideBoxLayout;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideToggleButton;
import org.apache.log4j.Logger;
import org.broad.igv.Globals;
import org.broad.igv.PreferenceManager;
import org.broad.igv.feature.Chromosome;
import org.broad.igv.feature.Cytoband;
import org.broad.igv.feature.FeatureDB;
import org.broad.igv.feature.NamedFeature;
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.feature.genome.GenomeListItem;
import org.broad.igv.feature.genome.GenomeManager;
import org.broad.igv.feature.genome.GenomeServerException;
import org.broad.igv.session.History;
import org.broad.igv.ui.action.FitDataToWindowMenuAction;
import org.broad.igv.ui.action.SearchCommand;
import org.broad.igv.ui.event.ViewChange;
import org.broad.igv.ui.panel.FrameManager;
import org.broad.igv.ui.panel.IGVPopupMenu;
import org.broad.igv.ui.panel.ReferenceFrame;
import org.broad.igv.ui.panel.ZoomSliderPanel;
import org.broad.igv.ui.util.*;
import org.broad.igv.ui.util.ProgressMonitor;
import org.broad.igv.util.LongRunningTask;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.NoRouteToHostException;
import java.util.*;
import java.util.List;

/**
 * @author jrobinso
 */
public class IGVCommandBar extends javax.swing.JPanel {

    private static Logger log = Logger.getLogger(IGVCommandBar.class);

    final static String MODIFY_DETAILS_TOOLTIP = "Modify popup text behavior in data panels";

    private JComboBox chromosomeComboBox;
    private JComboBox genomeComboBox;
    //private JPanel geneListPanel;
    // private JideButton geneListLabel;
    private JideButton goButton;
    private JideButton homeButton;
    private JPanel locationPanel;
    private JideButton refreshButton;
    private JideToggleButton roiToggleButton;
    private JideButton detailsBehaviorButton;
    private JTextField searchTextField;
    private JPanel toolPanel;
    private JPanel zoomControl;
    final private int DEFAULT_CHROMOSOME_DROPDOWN_WIDTH = 120;
    private JideButton backButton;
    private JideButton forwardButton;
    private JideButton fitToWindowButton;

    private JideButton exomeButton;

    public enum SHOW_DETAILS_BEHAVIOR {
        HOVER("Show Details on Hover"), CLICK("Show Details on Click"), NEVER("Never Show Details");

        private final String label;

        private SHOW_DETAILS_BEHAVIOR(String label) {
            this.label = label;
        }

        public String getLabel() {
            return this.label;
        }
    }

    private SHOW_DETAILS_BEHAVIOR detailsBehavior = SHOW_DETAILS_BEHAVIOR.valueOf((PreferenceManager.getInstance().get(PreferenceManager.DETAILS_BEHAVIOR_KEY,
            SHOW_DETAILS_BEHAVIOR.HOVER.name()).toUpperCase()));

    public SHOW_DETAILS_BEHAVIOR getDetailsBehavior() {
        return detailsBehavior;
    }

    public IGVCommandBar() {
        initComponents();

        // Initialize controls
        SearchHints hints = new SearchHints(this.searchTextField);

        String currentChr = getDefaultReferenceFrame().getChrName();
        boolean isWholeGenome = currentChr.equals(Globals.CHR_ALL);

        chromosomeComboBox.setSelectedItem(currentChr);
        roiToggleButton.setEnabled(!isWholeGenome);
        zoomControl.setEnabled(!isWholeGenome);

        detailsBehaviorButton.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                getPopupMenuToolTipBehavior().show(e.getComponent(), e.getX(), e.getY());
            }
        });

        getDefaultReferenceFrame().getEventBus().register(this);
    }

    private JPopupMenu getPopupMenuToolTipBehavior() {
        final JPopupMenu popup = new IGVPopupMenu();
        for (final SHOW_DETAILS_BEHAVIOR behavior : SHOW_DETAILS_BEHAVIOR.values()) {
            JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(behavior.getLabel());
            menuItem.setSelected(detailsBehavior == behavior);
            menuItem.addActionListener(new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    detailsBehavior = behavior;
                    PreferenceManager.getInstance().put(PreferenceManager.DETAILS_BEHAVIOR_KEY, behavior.name());
                }
            });
            popup.add(menuItem);
        }
        return popup;
    }

    /**
     * This method is called once on startup
     *
     * @param monitor
     * @throws FileNotFoundException
     * @throws NoRouteToHostException
     */
    public void initializeGenomeList(final ProgressMonitor monitor)
            throws FileNotFoundException, NoRouteToHostException {

        if (log.isDebugEnabled()) {
            log.debug("Enter initializeGenomeList");
        }

        if (monitor != null) {
            monitor.fireProgressChange(1);
        }

        genomeComboBox.removeAllItems();
        genomeComboBox.setRenderer(new ComboBoxRenderer());
        genomeComboBox.setToolTipText(UIConstants.CHANGE_GENOME_TOOLTIP);

        GenomeManager.getInstance().buildGenomeItemList();
        refreshGenomeListComboBox();

        if (monitor != null) {
            monitor.fireProgressChange(50);
        }

        genomeComboBox.addActionListener(new GenomeBoxActionListener());

        // Post creation widget setup.
        searchTextField.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent actionevent) {
                goButtonActionPerformed(actionevent);
            }
        });

        if (log.isDebugEnabled()) {
            log.debug("Exit initializeGenomeList");
        }

    }

    public void updateComponentStates() {

        if (exomeButton != null) {
            exomeButton.setEnabled(!getDefaultReferenceFrame().getChrName().equalsIgnoreCase("all"));
        }
    }

    private void loadGenomeListItem(final GenomeListItem genomeListItem) {
        final Runnable runnable = new Runnable() {

            public void run() {
                if (genomeListItem != null) {
                    final IGV igv = IGV.getInstance();

                    //User selected "more", pull up dialog and revert combo box
                    if (genomeListItem == GenomeListItem.ITEM_MORE) {
                        selectGenome(GenomeManager.getInstance().getGenomeId());
                        IGV.getInstance().loadGenomeFromServerAction();
                        return;
                    }

                    // If we haven't changed genomes we're done.
                    if (genomeListItem.getId().equalsIgnoreCase(GenomeManager.getInstance().getGenomeId())) {
                        return;
                    }

                    final ProgressMonitor monitor = new ProgressMonitor();
                    final ProgressBar.ProgressDialog progressDialog = ProgressBar.showProgressDialog(IGV.getMainFrame(), "Loading Genome...", monitor, false);

                    try {
                        monitor.fireProgressChange(50);

                        Genome genome;

                        igv.resetSession(null);
                        genome = igv.getGenomeManager().loadGenome(genomeListItem.getLocation(), null);

                        updateChromosFromGenome(genome);
                        monitor.fireProgressChange(25);

                        genomeComboBox.setSelectedItem(genomeListItem);

                        monitor.fireProgressChange(25);

                        FrameManager.getDefaultFrame().setChromosomeName(genome.getHomeChromosome(), true);
                        IGV.getInstance().doRefresh();

                    } catch (GenomeServerException e) {
                        log.error("Error loading genome: " + genomeListItem.getLocation(), e);
                        JOptionPane.showMessageDialog(
                                IGV.getMainFrame(),
                                "Error loading genome: " + genomeListItem.getDisplayableName());
                    } catch (IOException e) {
                        int choice =
                                JOptionPane.showConfirmDialog(
                                        IGV.getMainFrame(), "The genome file [" + genomeListItem.getLocation() +
                                        "] could not be read. Would you like to remove the selected entry?",
                                        "", JOptionPane.OK_CANCEL_OPTION);

                        if (choice == JOptionPane.OK_OPTION) {
                            GenomeManager.getInstance().excludedUrl(genomeListItem.getLocation());
                            List<GenomeListItem> genomes = GenomeManager.getInstance().getGenomes();
                            genomes.remove(genomeListItem);
                            PreferenceManager.getInstance().saveGenomeIdDisplayList(genomes);

                            GenomeManager.getInstance().buildGenomeItemList();
                            GenomeManager.getInstance().updateImportedGenomePropertyFile();
                            refreshGenomeListComboBox();
                        }
                    } catch (Exception e) {
                        log.error("Error initializing genome", e);
                    } finally {
                        if (progressDialog != null) {
                            progressDialog.setVisible(false);
                        }
                    }

                }
            }
        };

        // If we're on the dispatch thread spawn a worker, otherwise just execute.
        LongRunningTask.submit(runnable);
    }

    class GenomeBoxActionListener implements ActionListener {

        public void actionPerformed(ActionEvent actionEvent) {
            Object selItem = genomeComboBox.getSelectedItem();
            if (!(selItem instanceof GenomeListItem)) {
                return;
            }
            GenomeListItem genomeListItem = (GenomeListItem) selItem;
            loadGenomeListItem(genomeListItem);
        }
    }

    void updateChromosomeDropdown() {

        final Genome genome = GenomeManager.getInstance().getCurrentGenome();
        if (genome == null) return;

        List<String> tmp = new ArrayList<String>(genome.getAllChromosomeNames().size());
        tmp.addAll(genome.getAllChromosomeNames());
        if (tmp.size() > 1) {
            String homeChr = genome.getHomeChromosome();
            if (homeChr.equals(Globals.CHR_ALL)) {
                tmp.add(0, Globals.CHR_ALL);
            }
        }

        Graphics2D graphics2D = (Graphics2D) chromosomeComboBox.getGraphics();
        Font font = chromosomeComboBox.getFont();
        FontMetrics fontMetrics = chromosomeComboBox.getFontMetrics(font);

        int w = DEFAULT_CHROMOSOME_DROPDOWN_WIDTH;
        for (String chromosomeName : tmp) {
            Rectangle2D textBounds = fontMetrics.getStringBounds(chromosomeName, graphics2D);
            if (textBounds != null) {
                int width = textBounds.getBounds().width + 50;

                // int width = chromosomeName.length()*fontSize-(fontSize*4);  // TODO Hack figure out whats's wrong with previous line
                if (width > w) {
                    w = width;
                }
            }
        }

        Object[] chomosomeNames = tmp.toArray();
        final DefaultComboBoxModel defaultModel = new DefaultComboBoxModel(chomosomeNames);
        final int dropdownWidth = w;

        chromosomeComboBox.setModel(defaultModel);
        chromosomeComboBox.setSelectedItem(genome.getHomeChromosome());

        UIUtilities.invokeOnEventThread(new Runnable() {

            public void run() {
                adjustChromosomeDropdownWidth(dropdownWidth);
            }
        });

    }

    /**
     * Update the command bar properties for the new chromosome name.
     * Will not cause a chromosome change, intended to be called in RESPONSE
     * to the chromosome changing
     *
     * @param chrName
     */
    protected void chromosomeChanged(final String chrName) {
        UIUtilities.invokeOnEventThread(new Runnable() {
            @Override
            public void run() {
                roiToggleButton.setEnabled(!Globals.CHR_ALL.equals(chrName));
                zoomControl.setEnabled(!Globals.CHR_ALL.equals(chrName));

                if (chromosomeComboBox.getSelectedItem() != null) {
                    if (!chromosomeComboBox.getSelectedItem().equals(chrName)) {
                        setChromosomeComboBoxNoActionListeners(chrName);
                    }
                }
            }
        });
    }


    public void updateCurrentCoordinates() {

        String p = "";

        final String chrName = getDefaultReferenceFrame().getChrName();
        if (!Globals.CHR_ALL.equals(chrName) && !FrameManager.isGeneListMode()) {
            p = getDefaultReferenceFrame().getFormattedLocusString();
        }
        final String position = p;
        final History history = IGV.getInstance().getSession().getHistory();

        UIUtilities.invokeOnEventThread(new Runnable() {
            public void run() {
                searchTextField.setText(position);
                forwardButton.setEnabled(history.canGoForward());
                backButton.setEnabled(history.canGoBack());
                roiToggleButton.setEnabled(!Globals.CHR_ALL.equals(chrName));
                zoomControl.setEnabled(!Globals.CHR_ALL.equals(chrName));
            }
        });


    }

    private ReferenceFrame getDefaultReferenceFrame() {
        return FrameManager.getDefaultFrame();
    }

    public void setGeneListMode(boolean geneListMode) {

        genomeComboBox.setEnabled(!geneListMode);
//        locationPanel.setEnabled(!geneListMode);
        chromosomeComboBox.setEnabled(!geneListMode);
//        searchTextField.setEnabled(!geneListMode);
//        goButton.setEnabled(!geneListMode);
        zoomControl.setEnabled(!geneListMode);
//        homeButton.setEnabled(true);
//        roiToggleButton.setEnabled(!geneListMode);
    }

    static class ComboBoxRenderer implements ListCellRenderer {

        JSeparator separator;

        /**
         * Constructs ...
         */
        public ComboBoxRenderer() {
            separator = new JSeparator(JSeparator.HORIZONTAL);
        }

        /**
         * Method description
         *
         * @param list
         * @param value
         * @param index
         * @param isSelected
         * @param cellHasFocus
         * @return
         */
        public Component getListCellRendererComponent(JList list, Object value, int index,
                                                      boolean isSelected, boolean cellHasFocus) {
            String text = (value == null) ? "" : value.toString();

            Component renderer = null;

            if (UIConstants.GENOME_LIST_SEPARATOR.equals(text)) {
                return separator;
            }

            if (text.equals(UIConstants.REMOVE_GENOME_LIST_MENU_ITEM)) {
                JLabel label = new JLabel(text);

                label.setOpaque(true);
                label.setBorder(new EmptyBorder(1, 1, 1, 1));
                renderer = label;
            } else {

                JLabel label = new JLabel(text);

                label.setOpaque(true);
                label.setBorder(new EmptyBorder(1, 1, 1, 1));
                label.setSize(label.getWidth() + 10, label.getHeight());
                renderer = label;
            }

            //We call with a null list when setting width
            if (list != null) {
                if (isSelected) {
                    renderer.setBackground(list.getSelectionBackground());
                    renderer.setForeground(list.getSelectionForeground());
                } else {
                    renderer.setBackground(list.getBackground());
                    renderer.setForeground(list.getForeground());
                }
                renderer.setFont(list.getFont());
            }


            return renderer;
        }
    }


    /**
     * Gets the collection of genome display names currently in use.
     *
     * @return Set of display names.
     */
    public Collection<String> getGenomeDisplayNames() {

        Set<String> displayNames = new HashSet<String>();
        Collection<GenomeListItem> listItems = GenomeManager.getInstance().getGenomes();
        for (GenomeListItem genomeListItem : listItems) {
            displayNames.add(genomeListItem.getDisplayableName());
        }
        return displayNames;
    }

    /**
     * Gets the collection of genome list items ids currently in use.
     *
     * @return Set of ids.
     */
    public Collection<String> getSelectableGenomeIDs() {

        Set<String> ids = new HashSet<String>();
        Collection<GenomeListItem> listItems = GenomeManager.getInstance().getGenomes();
        for (GenomeListItem genomeListItem : listItems) {
            ids.add(genomeListItem.getId());
        }
        return ids;
    }

    /**
     * Selects the first genome from the list which matches this genomeId.
     * If not found, checks genomes from the server/user-defined list
     *
     * @param genomeId
     */
    public void selectGenome(String genomeId) {
        if (!getSelectableGenomeIDs().contains(genomeId)) {
            // If genome archive was not found, check things not loaded
            //TODO Should we be doing this here?
            boolean found = false;
            try {
                found = GenomeManager.getInstance().loadFromArchive(genomeId);
            } catch (IOException e) {
                MessageUtils.showErrorMessage("Error checking server/cache for genomeId " + genomeId, e);
            }
            if (found) {
                refreshGenomeListComboBox();
            }
        }
        // Now select this item in the comboBox
        GenomeListItem matchingItem = GenomeManager.getInstance().getLoadedGenomeListItemById(genomeId);
        if (matchingItem != null) {
            genomeComboBox.setSelectedItem(matchingItem);
        }
    }

    public void updateChromosFromGenome(Genome genome) {

        for (Chromosome chr : genome.getChromosomes()) {
            final List<Cytoband> cytobands = chr.getCytobands();
            if (cytobands != null) {
                for (Cytoband cyto : cytobands) {
                    FeatureDB.addFeature(cyto.getLongName(), cyto, genome);
                }
            }
        }
        updateChromosomeDropdown();

    }

    public void refreshGenomeListComboBox() {
        genomeComboBox.setModel(getModelForGenomeListComboBox());
        String curId = GenomeManager.getInstance().getGenomeId();
        Object item = GenomeManager.getInstance().getLoadedGenomeListItemById(curId);
        genomeComboBox.setSelectedItem(item);
    }

    /**
     * Build a model for the genome combo box
     *
     * @return
     */
    private DefaultComboBoxModel getModelForGenomeListComboBox() {
        List<GenomeListItem> genomes = GenomeManager.getInstance().getGenomes();
        genomes.add(GenomeListItem.ITEM_MORE);
        return new DefaultComboBoxModel(genomes.toArray(new GenomeListItem[0]));
    }

    /**
     * This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {

        setMinimumSize(new Dimension(200, 32));

        // setPreferredSize(new Dimension(800, 32));

        JideBoxLayout layout = new JideBoxLayout(this, JideBoxLayout.X_AXIS);

        setLayout(layout);

        // This controls the vertical height of the command bar

        locationPanel = new javax.swing.JPanel();
        locationPanel.setBorder(new LineBorder(Color.lightGray, 1, true));

        // BorderFactory.createMatteBorder(2, 2, 2, 2, Color.lightGray));
        // new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        locationPanel.setPreferredSize(new java.awt.Dimension(150, 20));
        locationPanel.setLayout(new JideBoxLayout(locationPanel, JideBoxLayout.X_AXIS));
        locationPanel.setAlignmentY(CENTER_ALIGNMENT);
        locationPanel.add(Box.createRigidArea(new Dimension(10, 36)), JideBoxLayout.FIX);

        genomeComboBox = new JComboBox();
        genomeComboBox.setMinimumSize(new Dimension(180, 27));
        genomeComboBox.setPreferredSize(new Dimension(180, 27));

        genomeComboBox.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                try {
                    adjustPopupWidth(genomeComboBox);
                } catch (Exception e1) {
                    log.warn(e1.getMessage(), e1);
                }
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                //TODO
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
                //TODO
            }
        });

        locationPanel.add(genomeComboBox, JideBoxLayout.FIX);
        locationPanel.add(Box.createHorizontalStrut(5), JideBoxLayout.FIX);


        chromosomeComboBox = new javax.swing.JComboBox();
        chromosomeComboBox.setToolTipText("Select a chromosome to view");
        chromosomeComboBox.setMaximumSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 30));
        chromosomeComboBox.setMinimumSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 30));
        chromosomeComboBox.setPreferredSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 30));
        chromosomeComboBox.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                chromosomeComboBoxActionPerformed(evt);
            }
        });
        locationPanel.add(chromosomeComboBox, JideBoxLayout.FIX);
        locationPanel.add(Box.createHorizontalStrut(5), JideBoxLayout.FIX);

        searchTextField = new JTextField();
        searchTextField.setToolTipText("Enter a gene of locus, e.f. EGFR,   chr1,   or chr1:100,000-200,000");
        searchTextField.setMaximumSize(new java.awt.Dimension(250, 15));
        searchTextField.setMinimumSize(new java.awt.Dimension(100, 28));
        searchTextField.setPreferredSize(new java.awt.Dimension(230, 28));
        searchTextField.setAlignmentY(CENTER_ALIGNMENT);

        locationPanel.add(searchTextField, JideBoxLayout.FIX);

        goButton = new JideButton("Go");
        // goButton.setButtonStyle(ButtonStyle.TOOLBOX_STYLE);

        // goButton.setPreferredSize(new java.awt.Dimension(30, 30));
        // goButton.setMaximumSize(new java.awt.Dimension(30, 30));
        // goButton.setMinimumSize(new java.awt.Dimension(30, 30));
        // goButton.setText("Go");
        goButton.setToolTipText("Jump to gene or locus");
        goButton.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                goButtonActionPerformed(evt);
            }
        });
        locationPanel.add(goButton, JideBoxLayout.FIX);

        add(locationPanel, JideBoxLayout.FIX);

        add(Box.createHorizontalStrut(10), JideBoxLayout.FIX);

        toolPanel = new javax.swing.JPanel();
        toolPanel.setAlignmentX(RIGHT_ALIGNMENT);
        toolPanel.setLayout(new JideBoxLayout(toolPanel, JideBoxLayout.X_AXIS));
        //final Border toolButtonBorder = BorderFactory.createLineBorder(Color.gray, 1);

        homeButton = new com.jidesoft.swing.JideButton();
        homeButton.setAlignmentX(RIGHT_ALIGNMENT);
        //homeButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        // homeButton.setBorder(toolButtonBorder);
        homeButton.setIcon(new javax.swing.ImageIcon(
                getClass().getResource("/toolbarButtonGraphics/navigation/Home24.gif")));
        homeButton.setMaximumSize(new java.awt.Dimension(32, 32));
        homeButton.setMinimumSize(new java.awt.Dimension(32, 32));
        homeButton.setPreferredSize(new java.awt.Dimension(32, 32));
        homeButton.setToolTipText("Jump to whole genome view");
        homeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                homeButtonActionPerformed(evt);
            }
        });
        toolPanel.add(homeButton, JideBoxLayout.FIX);


        // toolPanel.setBorder(
        // new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        backButton = new JideButton();
        //backButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        //backButton.setBorder(toolButtonBorder);
        backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/left-arrow.gif")));
        backButton.setToolTipText("Go back");
        backButton.setMaximumSize(new java.awt.Dimension(32, 32));
        backButton.setMinimumSize(new java.awt.Dimension(32, 32));
        backButton.setPreferredSize(new java.awt.Dimension(32, 32));
        backButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                IGV.getInstance().getSession().getHistory().back();

            }
        });
        backButton.setEnabled(false);
        toolPanel.add(backButton, JideBoxLayout.FIX);

        forwardButton = new JideButton();
        //forwardButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        //forwardButton.setBorder(toolButtonBorder);
        forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/right-arrow.gif")));
        forwardButton.setToolTipText("Go forward");
        forwardButton.setMaximumSize(new java.awt.Dimension(32, 32));
        forwardButton.setMinimumSize(new java.awt.Dimension(32, 32));
        forwardButton.setPreferredSize(new java.awt.Dimension(32, 32));
        forwardButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                IGV.getInstance().getSession().getHistory().forward();
            }
        });
        forwardButton.setEnabled(false);
        toolPanel.add(forwardButton, JideBoxLayout.FIX);

        refreshButton = new com.jidesoft.swing.JideButton();
        //refreshButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        //refreshButton.setBorder(toolButtonBorder);
        refreshButton.setAlignmentX(RIGHT_ALIGNMENT);
        refreshButton.setIcon(new javax.swing.ImageIcon(
                getClass().getResource("/toolbarButtonGraphics/general/Refresh24.gif")));    // NOI18N
        refreshButton.setMaximumSize(new java.awt.Dimension(32, 32));
        refreshButton.setMinimumSize(new java.awt.Dimension(32, 32));
        refreshButton.setPreferredSize(new java.awt.Dimension(32, 32));
        refreshButton.setToolTipText("Refresh the screen");
        refreshButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                refreshButtonActionPerformed(evt);
            }
        });
        toolPanel.add(refreshButton, JideBoxLayout.FIX);


        Icon regionOfInterestIcon =
                IconFactory.getInstance().getIcon(IconFactory.IconID.REGION_OF_INTEREST);

        roiToggleButton = new JideToggleButton(regionOfInterestIcon);
        //roiToggleButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        //roiToggleButton.setBorder(toolButtonBorder);
        roiToggleButton.setAlignmentX(RIGHT_ALIGNMENT);
        roiToggleButton.setToolTipText("Define a region of interest.");
        roiToggleButton.setMaximumSize(new java.awt.Dimension(32, 32));
        roiToggleButton.setMinimumSize(new java.awt.Dimension(32, 32));
        roiToggleButton.setPreferredSize(new java.awt.Dimension(32, 32));
        roiToggleButton.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                roiToggleButtonActionPerformed(evt);
            }
        });
        toolPanel.add(roiToggleButton, JideBoxLayout.FIX);


        fitToWindowButton = new JideButton();
        //fitToWindowButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        //fitToWindowButton.setBorder(toolButtonBorder);
        fitToWindowButton.setAlignmentX(RIGHT_ALIGNMENT);
        fitToWindowButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/collapseall.gif")));
        fitToWindowButton.setMaximumSize(new java.awt.Dimension(32, 32));
        fitToWindowButton.setMinimumSize(new java.awt.Dimension(32, 32));
        fitToWindowButton.setPreferredSize(new java.awt.Dimension(32, 32));
        fitToWindowButton.setToolTipText("Resize tracks to fit in window.");
        fitToWindowButton.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                (new FitDataToWindowMenuAction(null, 0, IGV.getInstance())).actionPerformed(evt);
            }
        });
        toolPanel.add(fitToWindowButton, JideBoxLayout.FIX);

        final Icon noTooltipIcon = IconFactory.getInstance().getIcon(IconFactory.IconID.NO_TOOLTIP);
        final Icon tooltipIcon = IconFactory.getInstance().getIcon(IconFactory.IconID.TOOLTIP);
        detailsBehaviorButton = new JideButton(noTooltipIcon);

        //detailsBehaviorButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
        //detailsBehaviorButton.setBorder(toolButtonBorder);
        detailsBehaviorButton.setAlignmentX(RIGHT_ALIGNMENT);
        detailsBehaviorButton.setToolTipText(MODIFY_DETAILS_TOOLTIP);
        detailsBehaviorButton.setMaximumSize(new java.awt.Dimension(32, 32));
        detailsBehaviorButton.setMinimumSize(new java.awt.Dimension(32, 32));
        detailsBehaviorButton.setPreferredSize(new java.awt.Dimension(32, 32));
        toolPanel.add(detailsBehaviorButton, JideBoxLayout.FIX);

        boolean showExomeButton = Globals.isDevelopment();
        if (showExomeButton) {
            exomeButton = new JideButton();
            exomeButton.setButtonStyle(JideButton.TOOLBAR_STYLE);
            exomeButton.setText(FrameManager.isExomeMode() ? "Genome" : "Exome");
            exomeButton.setToolTipText("Click to toggle between 'exome' and 'genome' views");
            exomeButton.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent actionEvent) {

                    int modifiers = actionEvent.getModifiers();
                    boolean showTrackMenu = (modifiers & ActionEvent.ALT_MASK) > 0;

                    boolean newMode = !FrameManager.isExomeMode();
                    if (!FrameManager.setExomeMode(newMode, showTrackMenu)) return;
                    String label = newMode ? "Genome" : "Exome";
                    exomeButton.setText(label);
                    IGV.getInstance().resetFrames();
                }
            });
            toolPanel.add(exomeButton, JideBoxLayout.FIX);
        }

        this.add(toolPanel);

        this.add(Box.createHorizontalGlue(), JideBoxLayout.VARY);

        zoomControl = new ZoomSliderPanel();

        // zoomControl.setAlignmentX(RIGHT_ALIGNMENT);
        Dimension dimSize = new Dimension(200, 30);
        zoomControl.setPreferredSize(dimSize);
        zoomControl.setMinimumSize(dimSize);
        zoomControl.setMaximumSize(dimSize);
        zoomControl.setToolTipText("Click + to zoom in,  - to zoom out");
        zoomControl.setOpaque(false);
        this.add(zoomControl, JideBoxLayout.FIX);

        this.add(Box.createHorizontalStrut(20), JideBoxLayout.FIX);
    }

    /**
     * Method description
     *
     * @return
     */
    public GenomeListItem getGenomeSelectedInDropdown() {
        return (GenomeListItem) genomeComboBox.getSelectedItem();
    }

    private void adjustChromosomeDropdownWidth(int width) {

        int newWidth = (width > DEFAULT_CHROMOSOME_DROPDOWN_WIDTH)
                ? width : DEFAULT_CHROMOSOME_DROPDOWN_WIDTH;

        chromosomeComboBox.setMaximumSize(new java.awt.Dimension(newWidth, 35));
        chromosomeComboBox.setMinimumSize(new java.awt.Dimension(newWidth, 27));
        chromosomeComboBox.setPreferredSize(new java.awt.Dimension(newWidth, 16));
        revalidate();
    }

    /**
     * Adjust the popup for the combobox to be at least as wide as
     * the widest item.
     * @param box
     */
    private void adjustPopupWidth(JComboBox box) {
        if (box.getItemCount() == 0) return;
        Object comp = box.getUI().getAccessibleChild(box, 0);
        if (!(comp instanceof JPopupMenu)) {
            return;
        }
        JPopupMenu popup = (JPopupMenu) comp;
        JScrollPane scrollPane = null;
        for(Component scomp: popup.getComponents()){
            if(scomp instanceof JScrollPane){
                scrollPane = (JScrollPane) scomp;
            }
        }
        if(scrollPane == null) return;

        //Loop through and set width to widest component, plus some padding
        int rendererWidth = box.getWidth();
        for(int index=0; index < box.getItemCount(); index++){
            Object value = box.getItemAt(index);
            Component rendererComp = box.getRenderer().getListCellRendererComponent(null, value, index, false, false);
        }

        Dimension size = scrollPane.getPreferredSize();
        size.width = Math.max(size.width, rendererWidth);
        scrollPane.setPreferredSize(size);
        scrollPane.setMaximumSize(size);
        scrollPane.revalidate();
    }

    private void homeButtonActionPerformed(java.awt.event.ActionEvent evt) {
        Genome genome = GenomeManager.getInstance().getCurrentGenome();
        if (FrameManager.isGeneListMode()) {
            IGV.getInstance().setGeneList(null);
        }
        if (genome != null) {
            String chrName = genome.getHomeChromosome();
            if (chrName != null && !chrName.equals(chromosomeComboBox.getSelectedItem())) {
                ViewChange.ChromosomeChangeCause cause = new ViewChange.ChromosomeChangeCause(evt.getSource(), chrName);
                cause.setRecordHistory(true);
                getDefaultReferenceFrame().getEventBus().post(cause);
            }
        }
    }

    private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {
        IGV.getInstance().doRefresh();
        System.gc();
    }

    private void chromosomeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {
        JComboBox combobox = (JComboBox) evt.getSource();
        final String chrName = (String) combobox.getSelectedItem();
        if (chrName != null) {
            getDefaultReferenceFrame().getEventBus().post(new ViewChange.ChromosomeChangeCause(combobox, chrName));
        }
    }

    private void setChromosomeComboBoxNoActionListeners(String chrName) {
        ActionListener[] listeners = chromosomeComboBox.getActionListeners();
        for (ActionListener l : listeners) {
            chromosomeComboBox.removeActionListener(l);
        }

        chromosomeComboBox.setSelectedItem(chrName);

        for (ActionListener l : listeners) {
            chromosomeComboBox.addActionListener(l);
        }
    }

    @Subscribe
    public void receiveViewChangeResult(ViewChange.Result e) {
        String chrName = getDefaultReferenceFrame().getChrName();
        setChromosomeComboBoxNoActionListeners(chrName);
        updateCurrentCoordinates();
    }

    private void goButtonActionPerformed(java.awt.event.ActionEvent evt) {    // GEN-FIRST:event_goButtonActionPerformed
        String searchText = searchTextField.getText();
        searchByLocus(searchText);
    }


    public void searchByLocus(final String searchText) {

        if (log.isDebugEnabled()) {
            log.debug("Enter search by locus: " + searchText);
        }

        if ((searchText != null) && (searchText.length() > 0)) {
            searchTextField.setText(searchText);
            (new SearchCommand(getDefaultReferenceFrame(), searchText)).execute();
            //This is not necessary, since we receive a ViewChange.Result later
            //chromosomeComboBox.setSelectedItem(getDefaultReferenceFrame().getChrName());
        }

        if (log.isDebugEnabled()) {
            log.debug("Exit search by locus: " + searchText);
        }
    }


    private void roiToggleButtonActionPerformed(java.awt.event.ActionEvent evt) {    // GEN-FIRST:event_roiToggleButtonActionPerformed
        if (roiToggleButton.isSelected()) {
            IGV.getInstance().beginROI(roiToggleButton);
        } else {
            IGV.getInstance().endROI();
        }
    }

    private class SearchHints extends ListDataIntelliHints<String> {

        public SearchHints(JTextComponent jTextComponent) {
            super(jTextComponent, new String[]{});
        }

        @Override
        public void acceptHint(Object context) {
            String text = (String) context;
            super.acceptHint(context);
            searchByLocus(text);
        }

        @Override
        public boolean updateHints(Object context) {
            String text = (String) context;
            if (text.length() <= 1) {
                return false;
            } else {
                //TODO Uncomment to use comprehensive feature search, note that it should support partial matches
                //List<NamedFeature> features = SearchCommand.comprehensiveFeatureSearch(text);
                List<NamedFeature> features = FeatureDB.getFeaturesList(text, SearchCommand.SEARCH_LIMIT);
                final List<SearchCommand.SearchResult> results = SearchCommand.getResults(features);
                Object[] list = SearchCommand.getSelectionList(results, false);
                if (list.length >= 1) {
                    this.setListData(list);
                    return true;
                }
            }
            return false;
        }
    }
}
