/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org

This file is part of Gephi.

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.

Copyright 2011 Gephi Consortium. All rights reserved.

The contents of this file are subject to the terms of either the GNU
General Public License Version 3 only ("GPL") or the Common
Development and Distribution License("CDDL") (collectively, the
"License"). You may not use this file except in compliance with the
License. You can obtain a copy of the License at
http://gephi.org/about/legal/license-notice/
or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
specific language governing permissions and limitations under the
License.  When distributing the software, include this License Header
Notice in each file and include the License files at
/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
License Header, with the fields enclosed by brackets [] replaced by
your own identifying information:
"Portions Copyrighted [year] [name of copyright owner]"

If you wish your version of this file to be governed by only the CDDL
or only the GPL Version 3, indicate your decision by adding
"[Contributor] elects to include this software in this distribution
under the [CDDL or GPL Version 3] license." If you do not indicate a
single choice of license, a recipient has the option to distribute
your version of this file under either the CDDL, the GPL Version 3 or
to extend the choice of license to its licensees as provided above.
However, if you add GPL Version 3 code and therefore, elected the GPL
Version 3 license, then the option applies only if the new code is
made subject to such option by the copyright holder.

Contributor(s):

Portions Copyrighted 2011 Gephi Consortium.
*/

package org.gephi.ui.components.richtooltip;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.text.AttributedString;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities;

/**
 *
 * @author Mathieu Bastian
 */
class BasicRichTooltipPanelUI extends RichTooltipPanelUI {
	/**
	 * The associated tooltip panel.
	 */
	protected JRichTooltipPanel richTooltipPanel;

	protected JLabel titleLabel;

	protected java.util.List<JLabel> descriptionLabels;

	protected JLabel mainImageLabel;

	protected JSeparator footerSeparator;

	protected JLabel footerImageLabel;

	protected java.util.List<JLabel> footerLabels;

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
	 */
	public static ComponentUI createUI(JComponent c) {
		return new BasicRichTooltipPanelUI();
	}

	public BasicRichTooltipPanelUI() {
		this.descriptionLabels = new ArrayList<>();
		this.footerLabels = new ArrayList<>();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent)
	 */
	@Override
	public void installUI(JComponent c) {
		this.richTooltipPanel = (JRichTooltipPanel) c;
		super.installUI(this.richTooltipPanel);
		installDefaults();
		installComponents();
		installListeners();

		this.richTooltipPanel.setLayout(createLayoutManager());
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent)
	 */
	@Override
	public void uninstallUI(JComponent c) {
		uninstallListeners();
		uninstallComponents();
		uninstallDefaults();
		super.uninstallUI(this.richTooltipPanel);
	}

	/**
	 * Installs default settings for the associated rich tooltip panel.
	 */
	protected void installDefaults() {
		Border b = this.richTooltipPanel.getBorder();
		if (b == null || b instanceof UIResource) {
			Border toSet = UIManager.getBorder("RichTooltipPanel.border");
			if (toSet == null)
				toSet = new BorderUIResource.CompoundBorderUIResource(
						new LineBorder(FlamingoUtilities.getBorderColor()),
						new EmptyBorder(2, 4, 3, 4));
			this.richTooltipPanel.setBorder(toSet);
		}
		LookAndFeel.installProperty(this.richTooltipPanel, "opaque",
				Boolean.TRUE);
	}

	/**
	 * Installs listeners on the associated rich tooltip panel.
	 */
	protected void installListeners() {
	}

	/**
	 * Installs components on the associated rich tooltip panel.
	 */
	protected void installComponents() {
	}

	/**
	 * Uninstalls default settings from the associated rich tooltip panel.
	 */
	protected void uninstallDefaults() {
		LookAndFeel.uninstallBorder(this.richTooltipPanel);
	}

	/**
	 * Uninstalls listeners from the associated rich tooltip panel.
	 */
	protected void uninstallListeners() {
	}

	/**
	 * Uninstalls subcomponents from the associated rich tooltip panel.
	 */
	protected void uninstallComponents() {
		this.removeExistingComponents();
	}

	@Override
	public void update(Graphics g, JComponent c) {
		this.paintBackground(g);
		this.paint(g, c);
	}

	protected void paintBackground(Graphics g) {
		Color main = FlamingoUtilities.getColor(Color.gray,
				"Label.disabledForeground").brighter();
		Graphics2D g2d = (Graphics2D) g.create();
		g2d.setPaint(new GradientPaint(0, 0, FlamingoUtilities.getLighterColor(
				main, 0.9), 0, this.richTooltipPanel.getHeight(),
				FlamingoUtilities.getLighterColor(main, 0.4)));
		g2d.fillRect(0, 0, this.richTooltipPanel.getWidth(),
				this.richTooltipPanel.getHeight());
		g2d.setFont(FlamingoUtilities.getFont(this.richTooltipPanel,
				"Ribbon.font", "Button.font", "Panel.font"));
		g2d.dispose();
	}

	@Override
	public void paint(Graphics g, JComponent c) {
	}

	protected LayoutManager createLayoutManager() {
		return new RichTooltipPanelLayout();
	}

	protected class RichTooltipPanelLayout implements LayoutManager {
		@Override
		public void addLayoutComponent(String name, Component comp) {
		}

		@Override
		public void removeLayoutComponent(Component comp) {
		}

		@Override
		public Dimension minimumLayoutSize(Container parent) {
			return this.preferredLayoutSize(parent);
		}

		@Override
		public Dimension preferredLayoutSize(Container parent) {
			Insets ins = parent.getInsets();
			int gap = getLayoutGap();
			Font font = FlamingoUtilities.getFont(parent, "Ribbon.font",
					"Button.font", "Panel.font");

			// the main text gets 200 pixels. The width is defined
			// by this and the presence of the main text.
			// The height is defined based on the width and the
			// text broken into multiline paragraphs

			int descTextWidth = getDescriptionTextWidth();
			int width = ins.left + 2 * gap + descTextWidth + ins.right;
			RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo();
			FontRenderContext frc = new FontRenderContext(
					new AffineTransform(), true, false);
			if (tooltipInfo.getMainImage() != null) {
				width += tooltipInfo.getMainImage().getWidth(null);
			}

			int fontHeight = parent.getFontMetrics(font).getHeight();

			int height = ins.top;

			// The title label
			height += fontHeight + gap;

			// The description text
			int descriptionTextHeight = 0;
			for (String descText : tooltipInfo.getDescriptionSections()) {
				AttributedString attributedDescription = new AttributedString(
						descText);
				attributedDescription.addAttribute(TextAttribute.FONT, font);
				LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
						attributedDescription.getIterator(), frc);
				while (true) {
					TextLayout tl = lineBreakMeasurer.nextLayout(descTextWidth);
					if (tl == null)
						break;
					descriptionTextHeight += fontHeight;
				}
				// add an empty line after the paragraph
				descriptionTextHeight += fontHeight;
			}
			// remove the empty line after the last paragraph
			descriptionTextHeight -= fontHeight;

			if (tooltipInfo.getMainImage() != null) {
				height += Math.max(descriptionTextHeight, new JLabel(
						new ImageIcon(tooltipInfo.getMainImage()))
						.getPreferredSize().height);
			} else {
				height += descriptionTextHeight;
			}

			if ((tooltipInfo.getFooterImage() != null)
					|| (tooltipInfo.getFooterSections().size() > 0)) {
				height += gap;
				// The footer separator
				height += new JSeparator(JSeparator.HORIZONTAL)
						.getPreferredSize().height;

				height += gap;

				int footerTextHeight = 0;
				int availableWidth = descTextWidth;
				if (tooltipInfo.getFooterImage() != null) {
					availableWidth -= tooltipInfo.getFooterImage().getWidth(
							null);
				}
				if (tooltipInfo.getMainImage() != null) {
					availableWidth += tooltipInfo.getMainImage().getWidth(null);
				}
				for (String footerText : tooltipInfo.getFooterSections()) {
					AttributedString attributedDescription = new AttributedString(
							footerText);
					attributedDescription
							.addAttribute(TextAttribute.FONT, font);
					LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
							attributedDescription.getIterator(), frc);
					while (true) {
						TextLayout tl = lineBreakMeasurer
								.nextLayout(availableWidth);
						if (tl == null)
							break;
						footerTextHeight += fontHeight;
					}
					// add an empty line after the paragraph
					footerTextHeight += fontHeight;
				}
				// remove the empty line after the last paragraph
				footerTextHeight -= fontHeight;

				if (tooltipInfo.getFooterImage() != null) {
					height += Math.max(footerTextHeight, new JLabel(
							new ImageIcon(tooltipInfo.getFooterImage()))
							.getPreferredSize().height);
				} else {
					height += footerTextHeight;
				}
			}

			height += ins.bottom;
			return new Dimension(width, height);
		}

		@Override
		public void layoutContainer(Container parent) {
			removeExistingComponents();

			Font font = FlamingoUtilities.getFont(parent, "Ribbon.font",
					"Button.font", "Panel.font");
			Insets ins = richTooltipPanel.getInsets();
			int y = ins.top;
			RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo();
			FontRenderContext frc = new FontRenderContext(
					new AffineTransform(), true, false);
			int gap = getLayoutGap();

			int fontHeight = parent.getFontMetrics(font).getHeight();

			// The title label
			titleLabel = new JLabel(tooltipInfo.getTitle());
			titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
			richTooltipPanel.add(titleLabel);

			titleLabel.setBounds(ins.left, y,
					titleLabel.getPreferredSize().width, fontHeight);
			y += titleLabel.getHeight() + gap;

			// The main image
			int x = ins.left;
			if (tooltipInfo.getMainImage() != null) {
				mainImageLabel = new JLabel(new ImageIcon(tooltipInfo
						.getMainImage()));
				richTooltipPanel.add(mainImageLabel);
				mainImageLabel.setBounds(x, y, mainImageLabel
						.getPreferredSize().width, mainImageLabel
						.getPreferredSize().height);
				x += mainImageLabel.getWidth();
			}
			x += 2 * gap;

			// The description text
			int descLabelWidth = parent.getWidth() - x - ins.right;
			for (String descText : tooltipInfo.getDescriptionSections()) {
				AttributedString attributedDescription = new AttributedString(
						descText);
				attributedDescription.addAttribute(TextAttribute.FONT, font);
				LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
						attributedDescription.getIterator(), frc);
				int currOffset = 0;
				while (true) {
					TextLayout tl = lineBreakMeasurer
							.nextLayout(descLabelWidth);
					if (tl == null)
						break;
					int charCount = tl.getCharacterCount();
					String line = descText.substring(currOffset, currOffset
							+ charCount);

					JLabel descLabel = new JLabel(line);
					descriptionLabels.add(descLabel);
					richTooltipPanel.add(descLabel);
					descLabel.setBounds(x, y,
							descLabel.getPreferredSize().width, fontHeight);
					y += descLabel.getHeight();

					currOffset += charCount;
				}
				// add an empty line after the paragraph
				y += titleLabel.getHeight();
			}
			// remove the empty line after the last paragraph
			y -= titleLabel.getHeight();

			if (mainImageLabel != null) {
				y = Math.max(y, mainImageLabel.getY()
						+ mainImageLabel.getHeight());
			}

			if ((tooltipInfo.getFooterImage() != null)
					|| (tooltipInfo.getFooterSections().size() > 0)) {
				y += gap;
				// The footer separator
				footerSeparator = new JSeparator(JSeparator.HORIZONTAL);
				richTooltipPanel.add(footerSeparator);
				footerSeparator.setBounds(ins.left, y, parent.getWidth()
						- ins.left - ins.right, footerSeparator
						.getPreferredSize().height);

				y += footerSeparator.getHeight() + gap;

				// The footer image
				x = ins.left;
				if (tooltipInfo.getFooterImage() != null) {
					footerImageLabel = new JLabel(new ImageIcon(tooltipInfo
							.getFooterImage()));
					richTooltipPanel.add(footerImageLabel);
					footerImageLabel.setBounds(x, y, footerImageLabel
							.getPreferredSize().width, footerImageLabel
							.getPreferredSize().height);
					x += footerImageLabel.getWidth() + 2 * gap;
				}

				// The footer text
				int footerLabelWidth = parent.getWidth() - x - ins.right;
				for (String footerText : tooltipInfo.getFooterSections()) {
					AttributedString attributedDescription = new AttributedString(
							footerText);
					attributedDescription
							.addAttribute(TextAttribute.FONT, font);
					LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
							attributedDescription.getIterator(), frc);
					int currOffset = 0;
					while (true) {
						TextLayout tl = lineBreakMeasurer
								.nextLayout(footerLabelWidth);
						if (tl == null)
							break;
						int charCount = tl.getCharacterCount();
						String line = footerText.substring(currOffset,
								currOffset + charCount);

						JLabel footerLabel = new JLabel(line);
						footerLabels.add(footerLabel);
						richTooltipPanel.add(footerLabel);
						footerLabel.setBounds(x, y, footerLabel
								.getPreferredSize().width, fontHeight);
						y += footerLabel.getHeight();

						currOffset += charCount;
					}
					// add an empty line after the paragraph
					y += titleLabel.getHeight();
				}
				// remove the empty line after the last paragraph
				y -= titleLabel.getHeight();
			}
		}
	}

	protected int getDescriptionTextWidth() {
		return 200;
	}

	protected int getLayoutGap() {
		return 4;
	}

	protected void removeExistingComponents() {
		if (this.titleLabel != null) {
			this.richTooltipPanel.remove(this.titleLabel);
		}
		if (this.mainImageLabel != null) {
			this.richTooltipPanel.remove(this.mainImageLabel);
		}
		for (JLabel label : this.descriptionLabels)
			this.richTooltipPanel.remove(label);
		if (this.footerSeparator != null) {
			this.richTooltipPanel.remove(this.footerSeparator);
		}
		if (this.footerImageLabel != null) {
			this.richTooltipPanel.remove(this.footerImageLabel);
		}
		for (JLabel label : this.footerLabels)
			this.richTooltipPanel.remove(label);
	}
}

