package rst.pdfbox.layout.shape;

import java.io.IOException;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;

import rst.pdfbox.layout.text.Position;
import rst.pdfbox.layout.util.CompatibilityHelper;

/**
 * A rectangular shape with rounded corners.
 */
public class RoundRect extends AbstractShape {

    private final static float BEZ = 0.551915024494f;

    private final float cornerRadiusX;
    private final float cornerRadiusY;

    /**
     * Creates a rounded rect with equal radiuss for both x-axis and y-axis (quarter of a circle).
     * @param cornerRadius the radius of the corner circle.
     */
    public RoundRect(float cornerRadius) {
	this(cornerRadius, cornerRadius);
    }

    /**
     * Creates a rounded rect with potentially different radiuss for both x-axis and y-axis (quarter of an ellipse).
     * @param cornerRadiusX the radius in x-direction of the corner ellipse.
     * @param cornerRadiusY the radius in y-direction of the corner ellipse.
     */
    public RoundRect(float cornerRadiusX, float cornerRadiusY) {
	this.cornerRadiusX = cornerRadiusX;
	this.cornerRadiusY = cornerRadiusY;
    }

    @Override
    public void add(PDDocument pdDocument, PDPageContentStream contentStream,
	    Position upperLeft, float width, float height) throws IOException {
	
	addRoundRect(contentStream, upperLeft, width, height, cornerRadiusX, cornerRadiusY);
    }

    /**
     * create points clockwise starting in upper left corner
     * 
     * <pre>
     *     a          b
     *      ----------
     *     /          \
     *  h |            | c
     *    |            |
     *    |            |
     *   g \          / d
     *      ----------
     *     f          e
     * </pre>
     * 
     * @param contentStream the content stream.
     * @param upperLeft the upper left point
     * @param width the width
     * @param height the height
     * @param cornerRadiusX the corner radius in x direction
     * @param cornerRadiusY the corner radius in y direction
     * @throws IOException by pdfbox
     */
    protected void addRoundRect(PDPageContentStream contentStream,
	    Position upperLeft, float width, float height, float cornerRadiusX,
	    float cornerRadiusY) throws IOException {
	float nettoWidth = width - 2 * cornerRadiusX;
	float nettoHeight = height - 2 * cornerRadiusY;

	// top line
	Position a = new Position(upperLeft.getX() + cornerRadiusX,
		upperLeft.getY());
	Position b = new Position(a.getX() + nettoWidth, a.getY());
	// right line
	Position c = new Position(upperLeft.getX() + width, upperLeft.getY()
		- cornerRadiusY);
	Position d = new Position(c.getX(), c.getY() - nettoHeight);
	// bottom line
	Position e = new Position(
		upperLeft.getX() + width - cornerRadiusX, upperLeft.getY()
			- height);
	Position f = new Position(e.getX() - nettoWidth, e.getY());
	// left line
	Position g = new Position(upperLeft.getX(), upperLeft.getY() - height
		+ cornerRadiusY);
	Position h = new Position(g.getX(), upperLeft.getY()
		- cornerRadiusY);

	float bezX = cornerRadiusX * BEZ;
	float bezY = cornerRadiusY * BEZ;

	contentStream.moveTo(a.getX(), a.getY());
	addLine(contentStream, a.getX(), a.getY(), b.getX(), b.getY());
	CompatibilityHelper.curveTo(contentStream,b.getX() + bezX, b.getY(), c.getX(),
		c.getY() + bezY, c.getX(), c.getY());
	// contentStream.addLine(c.getX(), c.getY(), d.getX(), d.getY());
	addLine(contentStream, c.getX(), c.getY(), d.getX(), d.getY());
	CompatibilityHelper.curveTo(contentStream,d.getX(), d.getY() - bezY, e.getX() + bezX,
		e.getY(), e.getX(), e.getY());
	// contentStream.addLine(e.getX(), e.getY(), f.getX(), f.getY());
	addLine(contentStream, e.getX(), e.getY(), f.getX(), f.getY());
	CompatibilityHelper.curveTo(contentStream,f.getX() - bezX, f.getY(), g.getX(),
		g.getY() - bezY, g.getX(), g.getY());
	addLine(contentStream, g.getX(), g.getY(), h.getX(), h.getY());
	CompatibilityHelper.curveTo(contentStream, h.getX(), h.getY() + bezY, a.getX() - bezX,
		a.getY(), a.getX(), a.getY());
    }

    /**
     * Using lines won't give us a continuing path, which looks silly on fill.
     * So we are approximating lines with bezier curves... is there no better
     * way?
     */
    private void addLine(final PDPageContentStream contentStream, float x1,
	    float y1, float x2, float y2) throws IOException {
	float xMid = (x1 + x2) / 2f;
	float yMid = (y1 + y2) / 2f;
	CompatibilityHelper.curveTo1(contentStream, xMid, yMid, x2, y2);
    }

}