/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.jasperreports.engine;

import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;


/**
 * An area on an image.
 * 
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 * @version $Id: JRPrintImageArea.java 3033 2009-08-27 11:46:22Z teodord $
 * @see JRPrintImageAreaHyperlink
 */
public class JRPrintImageArea implements Serializable
{

	private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
	
	public final static byte SHAPE_DEFAULT = 0;
	public final static byte SHAPE_RECTANGLE = 1;
	public final static byte SHAPE_CIRCLE = 2;
	public final static byte SHAPE_POLYGON = 3;
	
	public final static String SHAPE_HTML_DEFAULT = "default";
	public final static String SHAPE_HTML_RECTANGLE = "rect";
	public final static String SHAPE_HTML_CIRCLE = "circle";
	public final static String SHAPE_HTML_POLYGON = "poly";
	
	private final static Map htmlShapes;
	
	static
	{
		htmlShapes = new HashMap();
		htmlShapes.put(SHAPE_HTML_DEFAULT, new Byte(SHAPE_DEFAULT));
		htmlShapes.put(SHAPE_HTML_RECTANGLE, new Byte(SHAPE_RECTANGLE));
		htmlShapes.put(SHAPE_HTML_CIRCLE, new Byte(SHAPE_CIRCLE));
		htmlShapes.put(SHAPE_HTML_POLYGON, new Byte(SHAPE_POLYGON));
	}
	
	
	/**
	 * Returns the shape constant corresponding the HTML are shape type.
	 * 
	 * @param htmlShape the HTML are shape type
	 * @return the corresponding shape constant
	 */
	public static byte getShape(String htmlShape)
	{
		Byte shape = (Byte) htmlShapes.get(htmlShape.toLowerCase());
		if (shape == null)
		{
			throw new JRRuntimeException("Unknown HTML image area shape \"" + htmlShape + "\"");
		}
		return shape.byteValue();
	}
	
	
	/**
	 * Returns the HTML shape type corresponding to a shape type.
	 * 
	 * @param shape the shape type
	 * @return the HTML shape type
	 */
	public static String getHtmlShape(byte shape)
	{
		String htmlShape;
		switch (shape)
		{
			case SHAPE_DEFAULT:
				htmlShape = SHAPE_HTML_DEFAULT;
				break;
			case SHAPE_RECTANGLE:
				htmlShape = SHAPE_HTML_RECTANGLE;
				break;
			case SHAPE_CIRCLE:
				htmlShape = SHAPE_HTML_CIRCLE;
				break;
			case SHAPE_POLYGON:
				htmlShape = SHAPE_HTML_POLYGON;
				break;
			default:
				throw new JRRuntimeException("Unknown image area shape " + shape + "");
		}
		return htmlShape;
	}
	
	private byte shape = SHAPE_DEFAULT;
	private int[] coordinates;
	
	private transient Shape cachedAWTShape;
	
	/**
	 * Creates a blank image area.
	 */
	public JRPrintImageArea()
	{
	}


	/**
	 * Returns the shape type.
	 * 
	 * @return the shape type
	 */
	public byte getShape()
	{
		return shape;
	}

	
	/**
	 * Sets the area shape type.
	 * 
	 * @param shape the shape type, one of
	 * <ul>
	 * <li>{@link #SHAPE_DEFAULT SHAPE_DEFAULT}</li>
	 * <li>{@link #SHAPE_RECTANGLE SHAPE_RECTANGLE}</li>
	 * <li>{@link #SHAPE_POLYGON SHAPE_POLYGON}</li>
	 * <li>{@link #SHAPE_CIRCLE SHAPE_CIRCLE}</li>
	 * </ul>
	 */
	public void setShape(byte shape)
	{
		this.shape = shape;
	}

	
	/**
	 * Returns the shape coordinates.
	 * 
	 * @return the shape coordinates
	 */
	public int[] getCoordinates()
	{
		return coordinates;
	}

	
	/**
	 * Sets the shape coordinates.
	 * 
	 * @param coordinates the shape coordinates
	 */
	public void setCoordinates(int[] coordinates)
	{
		this.coordinates = coordinates;
	}
	
	
	/**
	 * Decides whether a specific point is inside this area.
	 * 
	 * @param x the X coordinate of the point
	 * @param y the Y coordinate of the point
	 * @return whether the point is inside this area
	 */
	public boolean containsPoint(int x, int y)
	{
		boolean contains;
		if (hasAWTShape())
		{
			ensureAWTShape();
			contains = cachedAWTShape.contains(x, y);
		}
		else
		{
			contains = true;
		}
		return contains;
	}
	
	
	protected void ensureAWTShape()
	{
		if (cachedAWTShape == null)
		{
			cachedAWTShape = createAWTShape();
		}
	}
	
	
	protected boolean hasAWTShape()
	{
		return shape != SHAPE_DEFAULT;
	}
	
	
	protected Shape createAWTShape()
	{
		Shape awtShape;
		switch (shape)
		{
			case SHAPE_RECTANGLE:
				awtShape = createAWTRectangle();
				break;
			case SHAPE_CIRCLE:
				awtShape = createAWTCircle();
				break;
			case SHAPE_POLYGON:
				awtShape = createAWTPolygon();
				break;
			default:
				awtShape = null;
				break;
		}
		return awtShape;
	}


	protected Shape createAWTRectangle()
	{
		if (coordinates == null || coordinates.length != 4)
		{
			throw new JRRuntimeException("A rectangle must have exactly 4 coordinates");
		}
		
		return new Rectangle(
				coordinates[0], 
				coordinates[1], 
				coordinates[2] - coordinates[0],
				coordinates[3] - coordinates[1]);
	}


	private Shape createAWTCircle()
	{
		if (coordinates == null || coordinates.length != 3)
		{
			throw new JRRuntimeException("A circle must have exactly 4 coordinates");
		}
		
		return new Ellipse2D.Float(coordinates[0], coordinates[1], coordinates[2], coordinates[2]);
	}


	private Shape createAWTPolygon()
	{
		if (coordinates == null || coordinates.length == 0 || coordinates.length % 2 != 0)
		{
			throw new JRRuntimeException("A polygon must have an even number of coordinates");
		}
		
		Polygon polygon = new Polygon();
		
		int i;
		for (i = 0; i < coordinates.length - 2; i += 2)
		{
			polygon.addPoint(coordinates[i], coordinates[i + 1]);
		}
		if (coordinates[i] != coordinates[0] || coordinates[i + 1] != coordinates[1])
		{
			polygon.addPoint(coordinates[i], coordinates[i + 1]);
		}

		return polygon;
	}
	
}
