/*
 * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.sun.swingset3;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;

import com.sun.swingset3.utilities.ArrowIcon;
import com.sun.swingset3.utilities.CollapsiblePanel;
import com.sun.swingset3.utilities.GradientPanel;
import com.sun.swingset3.utilities.Utilities;

import freeseawind.swing.LuckPanel;
import freeseawind.swing.LuckScrollPane;

/**
 *
 * @author Administrator
 */
public class DemoSelectorPanel extends JPanel {    
    private static final Border chiselBorder = new ChiselBorder();
    private static final Border panelBorder = new CompoundBorder(
            chiselBorder, new EmptyBorder(6,8,6,0));
    private static final Border categoryBorder = new CompoundBorder(
            chiselBorder, new EmptyBorder(0,0,10,0));    
    private static final Border buttonBorder = new CompoundBorder(
            new DemoButtonBorder(), new EmptyBorder(0, 18, 0, 0)); 
    
    private GradientPanel titlePanel;
    private JLabel demoListLabel;
    private JPanel viewPanel;
    private JScrollPane scrollPane;
    // need to track components that have defaults customizations
    private final List<CollapsiblePanel> collapsePanels = new ArrayList<CollapsiblePanel>();
    private Icon expandedIcon;
    private Icon collapsedIcon;
    private Color visitedForeground;
    private Color failedForeground;
       
    private ButtonGroup group;
    private final ActionListener demoActionListener = new DemoActionListener();
    private int buttonHeight = 0;
    
    private Demo selectedDemo;
    
    public DemoSelectorPanel(String demoSetTitle, List<Demo> demoSet) {
        super(new BorderLayout());
        setOpaque(false);
        
        visitedForeground = new Color(100, 100, 150);
        failedForeground = new Color(245, 20, 80);
        
        // only one demo may be selected at a time
        group = new ButtonGroup();
        
        // create demo set title area at top
        add(createTitleArea(demoSetTitle), BorderLayout.NORTH);
        
        // create scrollable demo panel at bottom
        JComponent selector = createDemoSelector(demoSet);
        scrollPane = new LuckScrollPane(selector);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setBorder(null);
        add(scrollPane, BorderLayout.CENTER);
        
        applyDefaults();
    }
    
    protected JComponent createTitleArea(String demoSetTitle) {
        JPanel titleAreaPanel = new JPanel(new BorderLayout());
        titlePanel = new GradientPanel(
                UIManager.getColor(SwingSet3.TITLE_GRADIENT_COLOR1_KEY),
                UIManager.getColor(SwingSet3.TITLE_GRADIENT_COLOR2_KEY));
        titlePanel.setLayout(new BorderLayout());
        titlePanel.setBorder(panelBorder);
        demoListLabel = new JLabel(demoSetTitle);
        demoListLabel.setOpaque(false);
        demoListLabel.setHorizontalAlignment(JLabel.LEADING);
        titlePanel.add(demoListLabel, BorderLayout.CENTER);
        titleAreaPanel.add(titlePanel, BorderLayout.NORTH);        
        
        // Add panel with view combobox
        viewPanel = new JPanel();
        viewPanel.setLayout(new BoxLayout(viewPanel, BoxLayout.X_AXIS));
        viewPanel.setBorder(new CompoundBorder(chiselBorder,
                new EmptyBorder(12,8,12,8)));
        JLabel viewLabel = new JLabel("View:");
        viewPanel.add(viewLabel);
        viewPanel.add(Box.createHorizontalStrut(6));
        JComboBox viewComboBox = new JComboBox();
        viewComboBox.addItem("by category");
        viewPanel.add(viewComboBox);
        titleAreaPanel.add(viewPanel, BorderLayout.CENTER);
        
        return titleAreaPanel;
    }
        
    protected JComponent createDemoSelector(List<Demo> demoSet) {
        JPanel selectorPanel = new LuckPanel();
        
        selectorPanel.setBackground(Color.white);
        
        GridBagLayout gridbag = new GridBagLayout();
        selectorPanel.setLayout(gridbag);
        GridBagConstraints c = new GridBagConstraints(); 
        c.gridx = c.gridy = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1;
               
        HashMap<String,JPanel> categoryMap = new HashMap<String,JPanel>();
        GridBagLayout categoryGridbag = null;
        GridBagConstraints cc = new GridBagConstraints();
        cc.gridx = cc.gridy = 0;
        cc.weightx = 1;
        cc.fill = GridBagConstraints.HORIZONTAL;
        CollapsiblePanel collapsePanel;
        for(Demo demo: demoSet) {
            String category = demo.getCategory();
            JPanel categoryPanel = categoryMap.get(category);
            if (categoryPanel == null) {
                // Create category collapsible panel
                categoryPanel = new JPanel();
                categoryGridbag = new GridBagLayout();
                categoryPanel.setLayout(categoryGridbag);                
                collapsePanel = new CollapsiblePanel(categoryPanel, category, 
                        "click to expand or contract category");
                collapsePanels.add(collapsePanel);
                collapsePanel.setBorder(categoryBorder);
                categoryMap.put(category, categoryPanel);
                gridbag.addLayoutComponent(collapsePanel, c);
                selectorPanel.add(collapsePanel);
                c.gridy++;
            }
            DemoButton demoButton = new DemoButton(demo);
            categoryGridbag.addLayoutComponent(demoButton, cc);
            cc.gridy++;
            group.add(demoButton);
            categoryPanel.add(demoButton);
            if (buttonHeight == 0) {
                buttonHeight = demoButton.getPreferredSize().height;
            }
        }
        // add empty component to take up any extra room on bottom
        JPanel trailer = new JPanel();
        c.weighty = 1.0;
        gridbag.addLayoutComponent(trailer, c);
        selectorPanel.add(trailer);
        trailer.setOpaque(false);
        applyDefaults();
        
        return selectorPanel;
    }
   
    public void updateUI() {
        super.updateUI();
        applyDefaults();
    }
        
    protected void applyDefaults() {
        
        expandedIcon = new ArrowIcon(ArrowIcon.SOUTH,
                UIManager.getColor(SwingSet3.TITLE_FOREGROUND_KEY));
        collapsedIcon = new ArrowIcon(ArrowIcon.EAST,
                UIManager.getColor(SwingSet3.TITLE_FOREGROUND_KEY));
        
        setBorder(new MatteBorder(0,0,0,1, 
                UIManager.getColor(SwingSet3.CONTROL_MID_SHADOW_KEY)));
        
        if (titlePanel != null) {
            titlePanel.setGradientColor1(
                UIManager.getColor(SwingSet3.TITLE_GRADIENT_COLOR1_KEY));
            titlePanel.setGradientColor2(
                UIManager.getColor(SwingSet3.TITLE_GRADIENT_COLOR2_KEY));
        }

        if (demoListLabel != null) {
           demoListLabel.setForeground(UIManager.getColor(SwingSet3.TITLE_FOREGROUND_KEY));
           demoListLabel.setFont(UIManager.getFont(SwingSet3.TITLE_FONT_KEY));
        }
        if (viewPanel != null) {
            viewPanel.setBackground(UIManager.getColor(SwingSet3.SUB_PANEL_BACKGROUND_KEY));
        }        
        if (collapsePanels != null) {
            for (CollapsiblePanel collapsePanel : collapsePanels) {
                collapsePanel.setFont(
                        UIManager.getFont("CheckBox.font").deriveFont(Font.BOLD));
                collapsePanel.setForeground(UIManager.getColor(SwingSet3.TITLE_FOREGROUND_KEY));
                collapsePanel.setExpandedIcon(expandedIcon);
                collapsePanel.setCollapsedIcon(collapsedIcon);
            }
        }
        revalidate();
    }
    
    public Demo getSelectedDemo() {
        return selectedDemo;
    }
    
    protected void setSelectedDemo(Demo demo) {
        Demo oldSelectedDemo = selectedDemo;
        selectedDemo = demo;
        firePropertyChange("selectedDemo", oldSelectedDemo, demo);
    }
    
    private class DemoButton extends JToggleButton {
        private Demo demo;
        
        public DemoButton(Demo demo) {
            super();
            this.demo = demo;
            String demoName = demo.getName();
            if (demoName.endsWith("Demo")) {
                setText(demoName.substring(0, demoName.indexOf("Demo")));
            } else {
                setText(demoName);
            }
            setIcon(demo.getIcon());
            setIconTextGap(10);
            setHorizontalTextPosition(JToggleButton.TRAILING);
            setHorizontalAlignment(JToggleButton.LEADING);
            setOpaque(false);
            setBorder(buttonBorder);
            setFocusPainted(false);
            setContentAreaFilled(false);
            setToolTipText(demo.getShortDescription());
            addActionListener(demoActionListener);
        }
        
        @Override
        public void updateUI() {
            super.updateUI();
            // some look and feels replace our border, so take it back
            setBorder(buttonBorder);
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            if (isSelected()) {
                setBackground(UIManager.getColor("Tree.selectionBackground"));
                g.setColor(UIManager.getColor("Tree.selectionBackground"));
                Dimension size = getSize();
                g.fillRect(0, 0, size.width, size.height); 
                setForeground(UIManager.getColor("Tree.selectionForeground"));
            } else {
                setBackground(UIManager.getColor("ToggleButton.background"));
                Color foreground = UIManager.getColor("ToggleButton.foreground");
                switch(demo.getState()) {
                    case STOPPED: {
                        foreground = visitedForeground;
                        break;
                    }
                    case FAILED: {
                        foreground = failedForeground;
                    }
                }
                setForeground(foreground);
            }
            super.paintComponent(g);
        }
        
        public Demo getDemo() {
            return demo;
        }
    }
    
    private class DemoActionListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            DemoButton demoButton = (DemoButton)event.getSource();
            setSelectedDemo(demoButton.getDemo());
        }
    }
    
    private static class DemoButtonBorder implements Border {
        private Insets insets = new Insets(2, 1, 1, 1);
        
        public DemoButtonBorder() {            
        }
        
        public Insets getBorderInsets(Component c) {
            return insets;
        }
        public boolean isBorderOpaque() {
            return true;
        }
         public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            AbstractButton b = (AbstractButton)c;
            if (b.isSelected()) {
                Color color = c.getBackground();
                g.setColor(Utilities.deriveColorHSB(color, 0, 0, -.20f));
                g.drawLine(x, y, x + width, y);
                g.setColor(Utilities.deriveColorHSB(color, 0, 0, -.10f));
                g.drawLine(x, y + 1, x + width, y + 1);
                g.drawLine(x, y + 2, x, y + height - 2);
                g.setColor(Utilities.deriveColorHSB(color, 0, 0, .24f));
                g.drawLine(x, y + height - 1, x + width, y + height-1);
            }
        }
    }
    
    private static class ChiselBorder implements Border {
        private Insets insets = new Insets(1, 0, 1, 0);
        
        public ChiselBorder() {            
        }
        
        public Insets getBorderInsets(Component c) {
            return insets;
        }
        public boolean isBorderOpaque() {
            return true;
        }
         public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            Color color = c.getBackground();
            // render highlight at top
            g.setColor(Utilities.deriveColorHSB(color, 0, 0, .2f));
            g.drawLine(x, y, x + width, y);
            // render shadow on bottom
            g.setColor(Utilities.deriveColorHSB(color, 0, 0, -.2f));
            g.drawLine(x, y + height - 1, x + width, y + height - 1);
        }
    }
}