/**
 * This software is released as part of the Pumpernickel project.
 * 
 * All com.pump resources in the Pumpernickel project are distributed under the
 * MIT License:
 * https://github.com/mickleness/pumpernickel/raw/master/License.txt
 * 
 * More information about the Pumpernickel project is available here:
 * https://mickleness.github.io/pumpernickel/
 */
package com.pump.awt;

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.SystemColor;
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.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.pump.geom.ShapeBounds;

/**
 * A Paintable that renders text as shapes.
 */
public class TextBoxPaintable implements Paintable {
	static FontRenderContext frc = new FontRenderContext(new AffineTransform(),
			true, true);

	Shape untransformedShape;
	Rectangle untransformedBounds;
	String text;
	String[] lines;
	Paint paint;
	float insets;

	/**
	 * Create a TextBoxPaintable using the system's default text color.
	 * 
	 * @param string
	 *            the initial string to render in this text.
	 * @param font
	 *            the font for this text box
	 * @param maxWidth
	 *            the maximum width to impose on this text box
	 * @param insets
	 *            the new insets to use
	 */
	public TextBoxPaintable(String string, Font font, float maxWidth,
			float insets) {
		this(string, font, maxWidth, insets, null);
	}

	public TextBoxPaintable(String string, Font font, float maxWidth,
			float insets, Paint paint) {
		if (paint == null)
			paint = SystemColor.textText;
		this.insets = insets;
		this.text = string;
		this.paint = paint;

		List<String> lines = new ArrayList<String>();
		Map<TextAttribute, Object> attributes = new HashMap<TextAttribute, Object>();
		attributes.put(TextAttribute.FONT, font);
		AttributedString attrString = new AttributedString(string, attributes);
		LineBreakMeasurer lbm = new LineBreakMeasurer(attrString.getIterator(),
				frc);
		TextLayout tl = lbm.nextLayout(maxWidth);
		float dy = 0;
		GeneralPath path = new GeneralPath();
		int startIndex = 0;
		while (tl != null) {
			path.append(
					tl.getOutline(AffineTransform.getTranslateInstance(0, dy)),
					false);
			dy += tl.getAscent() + tl.getDescent() + tl.getLeading();
			int charCount = tl.getCharacterCount();
			lines.add(text.substring(startIndex, startIndex + charCount));
			startIndex += charCount;
			tl = lbm.nextLayout(maxWidth);
		}
		this.lines = lines.toArray(new String[lines.size()]);
		untransformedShape = path;
		Rectangle2D b = ShapeBounds.getBounds(untransformedShape);
		b.setFrame(b.getX(), b.getY(), b.getWidth() + 2 * insets, b.getHeight()
				+ 2 * insets);
		untransformedBounds = b.getBounds();
	}

	public int getLineCount() {
		return lines.length;
	}

	public String getLine(int a) {
		return lines[a];
	}

	public String getText() {
		return text;
	}

	public int getWidth() {
		return untransformedBounds.width;
	}

	public int getHeight() {
		return untransformedBounds.height;
	}

	public void paint(Graphics2D g) {
		g = (Graphics2D) g.create();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
				RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
				RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
				RenderingHints.VALUE_FRACTIONALMETRICS_ON);
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		g.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
				RenderingHints.VALUE_STROKE_PURE);

		g.translate(-untransformedBounds.x, -untransformedBounds.y);
		g.translate(insets, insets);
		g.setPaint(paint);
		g.fill(untransformedShape);
		g.dispose();
	}
}