/*******************************************************************************
 * Copyright (c) MOBAC developers
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package mobac.gui.components;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.LayoutManager;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;

import mobac.utilities.GBC;
import mobac.utilities.Utilities;

/**
 * Bases upon "TitleContainer" from project "swivel" http://code.google.com/p/swivel/ (LGPL license)
 * 
 * A {@code TitleContainer} is a simple container that provides an easily visible title.
 * 
 */
public class JCollapsiblePanel extends JPanel {

	private static final long serialVersionUID = 1L;

	protected static final int DEFAULT_TITLE_PADDING = 3;
	protected static final Color DEFAULT_TITLE_BACKGROUND_COLOR = Color.LIGHT_GRAY;
	protected static final Color DEFAULT_TITLE_COLOR = Color.BLACK;

	private static ImageIcon arrowClosed;
	private static ImageIcon arrowOpen;

	static {
		try {
			arrowClosed = Utilities.loadResourceImageIcon("arrow_closed.png");
			arrowOpen = Utilities.loadResourceImageIcon("arrow_open.png");
		} catch (Exception e) {
			arrowClosed = new ImageIcon();
			arrowOpen = new ImageIcon();
		}
	}

	// title
	protected final JLabel titleIcon;
	protected final JLabel titleLabel;
	protected final JPanel titlePanel;

	// main component
	protected Container contentContainer;

	// collapsing
	protected final CollapsingMouseListener collapsingMouseListener;
	private boolean isCollapsed;

	public JCollapsiblePanel(String title) {
		this(new JPanel(), title);
	}

	public JCollapsiblePanel(String title, LayoutManager layout) {
		this(new JPanel(layout), title);
		setName(title);
	}

	/**
	 * Constructs a {@code TitleContainer} that wraps the specified component and has the specified title.
	 * 
	 * @param container
	 *            the main container
	 * @param title
	 *            the title
	 */
	public JCollapsiblePanel(Container container, String title) {
		super();
		setName(title);
		titleIcon = new JLabel(arrowOpen);
		titleLabel = new JLabel(title + ":");
		titlePanel = new JPanel(new GridBagLayout());
		// mainComponentPanel = new JPanel(new GridBagLayout());
		collapsingMouseListener = new CollapsingMouseListener();

		titleIcon.setMinimumSize(new Dimension(40, 40));
		titleIcon.setPreferredSize(titleIcon.getPreferredSize());

		// set collapse behavior
		titlePanel.addMouseListener(collapsingMouseListener);

		// look and feel
		setTitleBackgroundColor(DEFAULT_TITLE_BACKGROUND_COLOR);
		setTitleColor(DEFAULT_TITLE_COLOR);
		setTitleBarPadding(DEFAULT_TITLE_PADDING);

		// layout
		fillTitlePanel();

		titlePanel.add(Box.createHorizontalGlue(), GBC.std().fill());
		setLayout(new BorderLayout());
		add(titlePanel, BorderLayout.NORTH);
		add(container, BorderLayout.CENTER);
		setContentContainer(container);
		setBorder(BorderFactory.createEtchedBorder());

		setTitlePanelPreferredSize();
	}

	protected void fillTitlePanel() {
		titlePanel.add(titleIcon, GBC.std());
		titlePanel.add(titleLabel, GBC.std().insets(5, 0, 1, 0));
	}

	private void setTitlePanelPreferredSize() {
		Dimension containerPreferredDimension = contentContainer.getLayout().preferredLayoutSize(contentContainer);
		Dimension titlePanelPreferredDimension = titlePanel.getPreferredSize();
		titlePanel.setPreferredSize(new Dimension(containerPreferredDimension.width,
				titlePanelPreferredDimension.height));
	}

	/**
	 * This method provides a programmatic way to collapse the container.
	 * 
	 * @param collapsed
	 *            whether the container should be collapsed or shown
	 */
	public void setCollapsed(boolean collapsed) {
		if (isCollapsed == collapsed) {
			return;
		}

		if (collapsed) {
			titleIcon.setIcon(arrowClosed);

			// We have to make sure that the panel width does not shrink because
			// of the hidden content of contentContainer
			setTitlePanelPreferredSize();
		} else {
			titleIcon.setIcon(arrowOpen);
		}
		contentContainer.setVisible(!collapsed);

		isCollapsed = collapsed;
		revalidate();
	}

	/**
	 * Gets whether the container is collapsed or not.
	 * 
	 * @return whether the container is collapsed or not
	 */
	public boolean isCollapsed() {
		return isCollapsed;
	}

	/**
	 * Sets the title of this container.
	 * 
	 * @param title
	 *            the title
	 */
	public void setTitle(String title) {
		if (title != null) {
			titleLabel.setText(title);
		} else {
			titleLabel.setText("");
		}
	}

	/**
	 * Gets the this container's title.
	 * 
	 * @return the title
	 */
	public String getTitle() {
		return titleLabel.getText();
	}

	/**
	 * Sets the main content container for this container.
	 * 
	 * @param container
	 *            the main content container
	 */
	public void setContentContainer(Container container) {
		// don't need to do anything if the main component hasn't changed
		if (container == this.contentContainer) {
			return;
		}

		// remove the main component
		if (this.contentContainer != null) {
			remove(this.contentContainer);
		}

		// replace the main component
		this.contentContainer = container;
		add(container, BorderLayout.CENTER);

		// repaint main component
		revalidate();
	}

	/**
	 * Gets the main content container for this container.
	 * 
	 * @return the main container
	 */
	public Container getContentContainer() {
		return contentContainer;
	}

	public void addContent(Component comp, Object constraints) {
		contentContainer.add(comp, constraints);
	}

	/**
	 * Sets the title font.
	 * 
	 * @param font
	 *            the title font
	 */
	public void setTitleFont(Font font) {
		super.setFont(font);
		if (titleLabel != null) {
			titleLabel.setFont(font);
		}
	}

	/**
	 * Sets the background color of the title bar.
	 * 
	 * @param color
	 *            the color
	 */
	public void setTitleBackgroundColor(Color color) {
		this.titlePanel.setBackground(color);
	}

	/**
	 * Sets the title text color.
	 * 
	 * @param color
	 *            the color
	 */
	public void setTitleColor(Color color) {
		this.titleLabel.setForeground(color);
	}

	/**
	 * Sets the title bar padding in pixels.
	 * 
	 * @param padding
	 *            the title bar padding in pixels
	 */
	public void setTitleBarPadding(int padding) {
		Border border = BorderFactory.createEmptyBorder(padding, padding, padding, padding);
		this.titlePanel.setBorder(border);
	}

	/**
	 * Sets the visibility of the title bar. If the title bar is invisible, the user will not be able to collapse or
	 * decollapse the container.
	 * 
	 * @param visible
	 *            visibility of the title bar
	 */
	public void setTitleBarVisible(boolean visible) {
		this.titlePanel.setVisible(visible);
	}

	/**
	 * Gets the visibility of the title bar.
	 * 
	 * @return true if the title bar is visible, false otherwise
	 */
	public boolean isTitleBarVisible() {
		return this.titlePanel.isVisible();
	}

	// --------------------------------------------------------------------------

	/**
	 * A {@code MouseListener} that changes the cursor when moved over the title bar to indicate that it is clickable.
	 * Clicking the title bar collapses the container.
	 */
	private class CollapsingMouseListener extends MouseAdapter {
		private final Cursor CLICK_ME_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);

		/**
		 * Collapses or shows the titled component.
		 * 
		 * {@inheritDoc}
		 */
		public void mousePressed(MouseEvent e) {
			setCollapsed(!JCollapsiblePanel.this.isCollapsed);
		}

		/**
		 * Changes the cursor to indicate clickability.
		 * 
		 * {@inheritDoc}
		 */
		public void mouseEntered(MouseEvent e) {
			titlePanel.setCursor(CLICK_ME_CURSOR);
		}

		/**
		 * Changes the cursor back to the default.
		 * 
		 * {@inheritDoc}
		 */
		public void mouseExited(MouseEvent e) {
			titlePanel.setCursor(Cursor.getDefaultCursor());
		}
	}

}
