/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.gui.panel.button;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.awt.event.ActionListener;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;

import net.sf.jaxodraw.util.JaxoConstants;
import net.sf.jaxodraw.util.JaxoLocalized;


/** The menu bar that holds the file, edit, options and help menus.
 * @since 2.0
 */
public class JaxoButtonPanel extends JPanel implements PropertyChangeListener,
    JaxoLocalized {
    private static final long serialVersionUID = 7526471155622776147L;
    /** The raised etched border of all the panels holding the buttons. */
    public static final EtchedBorder RAISED_ETCHED_BORDER =
        (EtchedBorder) BorderFactory.createEtchedBorder(EtchedBorder.RAISED);

    /** The layout constraints of the buttons. */
    public static final GridBagConstraints BUTTON_CONSTRAINTS =
        new GridBagConstraints();

    // private panels
    private final JaxoParticleButtons pbtpanel;
    private final JaxoEditButtons ebtpanel;
    private final JaxoMiscButtons mbtpanel;
    private final JaxoActionButtons abtpanel;
    private final JaxoGridButtons gbtpanel;


    /**
     * Constructs the button panel.
     * Adds particle, edit, misc, action and grid button panels.
     *
     * @param l The ActionListener to receive events from this ButtonPanel.
     */
    public JaxoButtonPanel(final ActionListener l) {
        super(null, false);

        // Multipliers chosen by asthetics
        final int paddingWidth = JaxoPanelButton.getButtonHeight() / 6;
        final int paddingHeight = JaxoPanelButton.getButtonHeight() / 4;

        setLayout(new ButtonPanelLayout(paddingHeight * 2)); // should be even

        setBorder(BorderFactory.createCompoundBorder(RAISED_ETCHED_BORDER,
                BorderFactory.createEmptyBorder(paddingHeight, paddingWidth,
                    paddingHeight, paddingWidth)));

        pbtpanel = (JaxoParticleButtons) this.add(new JaxoParticleButtons());
        mbtpanel = (JaxoMiscButtons) this.add(new JaxoMiscButtons());
        abtpanel = (JaxoActionButtons) this.add(new JaxoActionButtons());
        ebtpanel = (JaxoEditButtons) this.add(new JaxoEditButtons());
        gbtpanel = (JaxoGridButtons) this.add(new JaxoGridButtons());

        setDefault(JaxoConstants.STANDBY);
        addPropertyChangeListeners();
        addActionListener(l);
    }

    /**
     * Paints additional etched lines in the middle between the children.
     *
     * @param g the graphics context.
     */
    @Override
    protected void paintBorder(final Graphics g) {
        super.paintBorder(g);

        boolean first = true;

        final int width = getWidth();

        int lastY = 0;

        for (int i = 0; i < getComponentCount(); i++) {
            final Component c = getComponent(i);

            if (!c.isVisible()) {
                continue;
            }

            if (first) {
                first = false;
            } else {
                final int y = (c.getY() + lastY) / 2;

                g.setColor(RAISED_ETCHED_BORDER.getHighlightColor(this));
                g.drawLine(1, y, width - 3, y);

                g.setColor(RAISED_ETCHED_BORDER.getShadowColor(this));
                g.drawLine(2, y + 1, width - 3, y + 1);
            }

            lastY = c.getY() + c.getHeight();
        }
    }

    /**
     * Applies a property change event.
     *
     * @param e the change event.
     */
    public void propertyChange(final PropertyChangeEvent e) {
        if (e.getPropertyName().equals("Jaxo.mode")) {
            pressButton(((Integer) (e.getNewValue())).intValue());
        } else if (e.getPropertyName().equals("Jaxo.defaultMode")) {
            setDefault(((Integer) (e.getNewValue())).intValue());
        } else {
            // dispatch to sub-panels
            firePropertyChange(e.getPropertyName(), e.getOldValue(),
                e.getNewValue());
        }
    }

    /** Adds the given ActionListener to all buttons in this panel.
    * @param l The ActionListener to add.
    */
    private void addActionListener(final ActionListener l) {
        pbtpanel.addActionListener(l);
        mbtpanel.addActionListener(l);
        abtpanel.addActionListener(l);
        ebtpanel.addActionListener(l);
        gbtpanel.addActionListener(l);
    }

    /** {@inheritDoc} */
    public final void updateLanguage() {
        pbtpanel.updateLanguage();
        mbtpanel.updateLanguage();
        abtpanel.updateLanguage();
        ebtpanel.updateLanguage();
        gbtpanel.updateLanguage();
    }

    /** Set a button as default.
     * @param mode An integer defined in {@link JaxoConstants JaxoConstants}
     * specifying the mode to be set as default.
     * Just delegates to the individual button panels.
     */
    public final void setDefault(final int mode) {
        ebtpanel.setDefault(mode);
        pbtpanel.setDefault(mode);
        mbtpanel.setDefault(mode);
        gbtpanel.setDefault(mode);
    }

    /** Presses a button.
     * @param mode An integer defined in {@link JaxoConstants JaxoConstants}
     * specifying the button to be pressed.
     * Just delegates to the individual button panels.
     */
    public final void pressButton(final int mode) {
        ebtpanel.pressButton(mode);
        pbtpanel.pressButton(mode);
        mbtpanel.pressButton(mode);
        gbtpanel.pressButton(mode);
    }

    private void addPropertyChangeListeners() {
        this.addPropertyChangeListener("Jaxo.vertexType", mbtpanel);
        this.addPropertyChangeListener(abtpanel);
        this.addPropertyChangeListener(gbtpanel);
    }

    // similar to a page BoxLayout, but the components are layed out from
    // the top with the available width and their preferred heights,
    // with 'gap' pixels inbetween.
    // This may be useful also elsewhere.
    private static class ButtonPanelLayout implements LayoutManager2 {
        private final int gap;

        ButtonPanelLayout(final int newGap) {
            this.gap = newGap;
        }

        public float getLayoutAlignmentX(final Container p) {
            return 0.5f;
        }

        public float getLayoutAlignmentY(final Container p) {
            return 0.5f;
        }

        public Dimension minimumLayoutSize(final Container p) {
            return preferredLayoutSize(p);
        }

        public Dimension maximumLayoutSize(final Container p) {
            return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
        }

        public void addLayoutComponent(final String constraints, final Component c) {
            // ignored
        }

        public void addLayoutComponent(final Component c, final Object constraints) {
            // ignored
        }

        public void removeLayoutComponent(final Component c) {
            // ignored
        }

        public void invalidateLayout(final Container p) {
            // ignored
        }

        public Dimension preferredLayoutSize(final Container p) {
            final Dimension result = new Dimension();

            int count = 0;

            for (int i = 0; i < p.getComponentCount(); i++) {
                final Component c = p.getComponent(i);

                if (!c.isVisible()) {
                    continue;
                }

                count++;

                final Dimension d = c.getPreferredSize();

                result.width = Math.max(result.width, d.width);
                result.height += d.height;
            }

            if (count > 0) {
                result.height += (gap * (count - 1));
            }

            final Insets n = p.getInsets();
            result.width += (n.left + n.right);
            result.height += (n.top + n.bottom);

            return result;
        }

        public void layoutContainer(final Container p) {
            final Insets n = p.getInsets();

            final int x = n.left;
            int y = n.top;
            final int width = p.getWidth() - n.left - n.right;

            for (int i = 0; i < p.getComponentCount(); i++) {
                final Component c = p.getComponent(i);

                if (!c.isVisible()) {
                    continue;
                }

                final Dimension d = c.getPreferredSize();

                c.setBounds(x, y, width, d.height);

                y += (d.height + gap);
            }
        }
    }
}
