// Copyright 2000-2007, FreeHEP.
package org.freehep.graphics2d;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;

/**
 * The drawing methods which are guaranteed to work for the various output
 * formats of the VectorGraphics system on the Java 2 platform. All methods are
 * re-declared abstract, since this class inherits from Graphics2D and we would
 * not want to actually or accidentally use any of those methods, except for the
 * ones noted.
 * 
 * Some int methods need to call their super.methods otherwise the compiler
 * cannot make a distinction if it needs to convert int to doubles or call the
 * super int method.
 * 
 * Note that many of these routines modify the current transformation matrix. To
 * guard against unintended side effects the following method should be used:
 * 
 * <pre><code>
 *  Graphics2D tempGraphics = (Graphics2D) originalGraphics.create();
 *  tempGraphics.setStroke(originalGraphics.getStroke());
 *  tempGraphics.rotate(...);
 *  tempGraphics.translate(...);
 *  ...drawing methods on tempGraphics...
 *  tempGraphics.dispose();
 * </code></pre>
 * 
 * where <code>originalGraphics</code> is the original <code>Graphics2D</code>
 * object. Note that <code>dispose</code> must be called when the drawing
 * finishes on <code>tempGraphics</code> and that no drawing should be done on
 * <code>originalGraphics</code> until <code>dispose</code> has been called.
 * 
 * @author Charles Loomis
 * @author Mark Donszelmann
 * @version $Id: freehep-graphics2d/src/main/java/org/freehep/graphics2d/VectorGraphics.java e5b4b9730048 2007/01/05 22:51:59 duns $
 */
public abstract class VectorGraphics extends Graphics2D implements
        VectorGraphicsConstants {

    public abstract void setProperties(Properties newProperties);

    protected abstract void initProperties(Properties defaults);

    protected abstract Properties getProperties();

    public abstract String getProperty(String key);

    public abstract Color getPropertyColor(String key);

    public abstract Rectangle getPropertyRectangle(String key);

    public abstract Dimension getPropertyDimension(String key);

    public abstract int getPropertyInt(String key);

    public abstract double getPropertyDouble(String key);

    public abstract boolean isProperty(String key);

    // //
    // Methods defined in java.awt.Graphics (alphabetical)
    // //

    public abstract void clearRect(int x, int y, int width, int height);

    public abstract void clipRect(int x, int y, int width, int height);

    public abstract void copyArea(int x, int y, int width, int height, int dx,
            int dy);

    public abstract Graphics create();

    // NOTE: implemented in Graphics, must be implemented here otherwise the
    // compiler
    // cannot choose between converting ints to doubles or calling the
    // superclass.
    public Graphics create(int x, int y, int width, int height) {
        return super.create(x, y, width, height);
    }

    public abstract void dispose();

    // NOTE: implemented in Graphics
    // public abstract void draw3DRect(int x, int y,
    // int width, int height,
    // boolean raised);
    public abstract void drawArc(int x, int y, int width, int height,
            int startAngle, int arcAngle);

    // NOTE: implemented in Graphics
    // public abstract void drawBytes(byte[] data, int offset,
    // int length,
    // int x, int y);
    // NOTE: implemented in Graphics
    // public abstract void drawChars(char[] data, int offset,
    // int length,
    // int x, int y);
    public abstract boolean drawImage(Image image, int x, int y,
            ImageObserver observer);

    public abstract boolean drawImage(Image image, int x, int y, int width,
            int height, ImageObserver observer);

    public abstract boolean drawImage(Image image, int x, int y, Color bgColor,
            ImageObserver observer);

    public abstract boolean drawImage(Image image, int x, int y, int width,
            int height, Color bgColor, ImageObserver observer);

    public abstract boolean drawImage(Image image, int dx1, int dy1, int dx2,
            int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer);

    public abstract boolean drawImage(Image image, int dx1, int dy1, int dx2,
            int dy2, int sx1, int sy1, int sx2, int sy2, Color bgColor,
            ImageObserver observer);

    public abstract void drawLine(int x1, int y1, int x2, int y2);

    public abstract void drawOval(int x, int y, int width, int height);

    public abstract void drawPolygon(int[] xPoints, int[] yPoints, int nPoints);

    // NOTE implemented in Graphics
    // public abstract void drawPolygon(Polygon p);
    public abstract void drawPolyline(int[] xPoints, int[] yPoints, int nPoints);

    public abstract void drawRect(int x, int y, int width, int height);

    public abstract void drawRoundRect(int x, int y, int width, int height,
            int arcWidth, int arcHeight);

    public abstract void drawString(AttributedCharacterIterator iterator,
            int x, int y);

    public abstract void drawString(String str, int x, int y);

    // NOTE: implemented in Graphics
    // public abstract void fill3DRect(int x, int y,
    // int width, int height,
    // boolean raised);
    public abstract void fillArc(int x, int y, int width, int height,
            int startAngle, int arcAngle);

    public abstract void fillOval(int x, int y, int width, int height);

    public abstract void fillPolygon(int[] xPoints, int[] yPoints, int nPoints);

    // NOTE: implemented in Graphics
    // public abstract void fillPolygon(Polygon p);
    public abstract void fillRect(int x, int y, int width, int height);

    public abstract void fillRoundRect(int x, int y, int width, int height,
            int arcWidth, int arcHeight);

    // NOTE: implemented in Graphics
    // public abstract void finalize();
    public abstract Shape getClip();

    public abstract Rectangle getClipBounds();

    public abstract Rectangle getClipBounds(Rectangle r);

    // NOTE: implemented in Graphics
    // public abstract Rectangle getClipRect();
    public abstract Color getColor();

    public abstract Font getFont();

    // NOTE: implemented in Graphics
    // public abstract FontMetrics getFontMetrics();
    public abstract FontMetrics getFontMetrics(Font font);

    // NOTE: implemented in Graphics
    // public abstract boolean hitClip(int x, int y, int width, int height);
    public abstract void setClip(int x, int y, int width, int height);

    public abstract void setClip(Shape clip);

    public abstract void setColor(Color c);

    public abstract void setFont(Font font);

    public abstract void setPaintMode();

    public abstract void setXORMode(Color c1);

    public abstract String toString();

    public abstract void translate(int x, int y);

    // //
    // Methods from java.awt.Graphics2D (alphabetical)
    // //
    public abstract void addRenderingHints(Map<?, ?> hints);

    public abstract void clip(Shape s);

    public abstract void draw(Shape s);

    // NOTE: overridden in Graphics2D
    // public abstract void draw3DRect(int x, int y, int width, int height,
    // boolean raised);
    public abstract void drawGlyphVector(GlyphVector g, float x, float y);

    public abstract void drawImage(BufferedImage img, BufferedImageOp op,
            int x, int y);

    public abstract boolean drawImage(Image img, AffineTransform xform,
            ImageObserver obs);

    public abstract void drawRenderableImage(RenderableImage img,
            AffineTransform xform);

    public abstract void drawRenderedImage(RenderedImage img,
            AffineTransform xform);

    public abstract void drawString(AttributedCharacterIterator iterator,
            float x, float y);

    // NOTE: overridden in Graphics2D
    // public abstract void drawString(AttributedCharacterIterator iterator, int
    // x, int y);
    // NOTE: redefined in Graphics2D
    // public abstract void drawString(String str, int x, int y);
    public abstract void drawString(String str, float x, float y);

    public abstract void fill(Shape s);

    /**
     * Fills an are with the given paint using in offscreen BufferedImage.
     * Used for drawing GradientPaint or image
     * @param shape Shape usede as clipping area
     * @param paint Paint used
     */
    protected void fill(Shape shape, Paint paint) {
        Rectangle2D bounds = shape.getBounds2D();

        // create image
        BufferedImage image = new BufferedImage(
            (int)Math.ceil(bounds.getWidth()) + 1,
            (int)Math.ceil(bounds.getHeight()) + 1,
            BufferedImage.TYPE_INT_ARGB);

        // fill background
        Graphics2D graphics = image.createGraphics();
        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
        graphics.fill(graphics.getDeviceConfiguration().getBounds());
        graphics.setComposite(AlphaComposite.SrcOver);

        // draw paint
        graphics.setPaint(paint);
        graphics.translate(- bounds.getMinX(), - bounds.getMinY());
        graphics.fill(shape);
        graphics.dispose();

        // draw image
        Shape clip = getClip();
        clip(shape);
        drawImage(image, (int)bounds.getX(), (int)bounds.getY(), null);
        setClip(clip);
    }

    // NOTE: overridden in Graphics2D
    // public abstract void fill3DRect(int x, int y,
    // int width, int height,
    // boolean raised);
    public abstract Color getBackground();

    public abstract Composite getComposite();

    public abstract GraphicsConfiguration getDeviceConfiguration();

    public abstract FontRenderContext getFontRenderContext();

    public abstract Paint getPaint();

    public abstract Object getRenderingHint(RenderingHints.Key inteKey);

    public abstract RenderingHints getRenderingHints();

    public abstract Stroke getStroke();

    public abstract AffineTransform getTransform();

    public abstract boolean hit(Rectangle rect, Shape s, boolean onStroke);

    public abstract void rotate(double theta);

    public abstract void rotate(double theta, double x, double y);

    public abstract void scale(double sx, double sy);

    public abstract void setBackground(Color color);

    public abstract void setComposite(Composite comp);

    public abstract void setPaint(Paint paint);

    public abstract void setRenderingHint(RenderingHints.Key hintKey,
            Object hintValue);

    public abstract void setRenderingHints(Map<?, ?> hints);

    public abstract void setStroke(Stroke s);

    public abstract void setTransform(AffineTransform xform);

    public abstract void shear(double shx, double shy);

    public abstract void transform(AffineTransform xform);

    public abstract void translate(double tx, double ty);

    // NOTE: redefines in Graphics2D
    // public abstract void translate(int x, int y);

    /*
     * =====================================================================================
     * 
     * Methods added to VectorGraphics (alphabetical)
     * 
     * =====================================================================================
     */

    public abstract void clearRect(double x, double y, double width,
            double height);

    public abstract void clipRect(double x, double y, double width,
            double height);

    public abstract Graphics create(double x, double y, double width,
            double height);

    /**
     * Draws an arc. Uses Arc2D to call draw(Shape).
     * 
     */
    public abstract void drawArc(double x, double y, double width,
            double height, double startAngle, double arcAngle);

    /**
     * Draws a straight line. Uses Line2D to call draw(Shape).
     * 
     */
    public abstract void drawLine(double x1, double y1, double x2, double y2);

    /**
     * Draws an oval. Uses Ellipse2D to call draw(Shape).
     * 
     */
    public abstract void drawOval(double x, double y, double width,
            double height);

    /**
     * Draws a polygon. Uses createShape(...) to call draw(Shape).
     * 
     */
    public abstract void drawPolygon(double[] xPoints, double[] yPoints,
            int nPoints);

    /**
     * Draws a polyline. Uses createShape(...) to call draw(Shape).
     * 
     */
    public abstract void drawPolyline(double[] xPoints, double[] yPoints,
            int nPoints);

    /**
     * Draws a rectangle. Uses Rectangle2D to call draw(Shape).
     * 
     */
    public abstract void drawRect(double x, double y, double width,
            double height);

    /**
     * Draws a rounded rectangle. Uses RoundRectangle2D to call draw(Shape).
     * 
     */
    public abstract void drawRoundRect(double x, double y, double width,
            double height, double arcWidth, double arcHeight);

    public abstract void drawSymbol(int x, int y, int size, int symbol);

    public abstract void drawSymbol(double x, double y, double size, int symbol);

    public abstract void fillSymbol(int x, int y, int size, int symbol);

    public abstract void fillSymbol(double x, double y, double size, int symbol);

    public abstract void fillAndDrawSymbol(int x, int y, int size, int symbol,
            Color fillColor);

    public abstract void fillAndDrawSymbol(double x, double y, double size,
            int symbol, Color fillColor);

    /**
     * Draws a string.
     * 
     */
    public abstract void drawString(String str, double x, double y);

    public abstract void drawString(TagString str, double x, double y);

    public abstract void drawString(String str, double x, double y,
            int horizontal, int vertical);

    public abstract void drawString(TagString str, double x, double y,
            int horizontal, int vertical);

    /**
     * Draws a string with a lot of parameters.
     * 
     * @param str text to be drawn
     * @param x coordinate to draw string
     * @param y coordinate to draw string
     * @param horizontal alignment of the text
     * @param vertical alignment of the text
     * @param framed true if text is surrounded by a frame
     * @param frameColor color of the frame
     * @param frameWidth witdh of the frame
     * @param banner true if the frame is filled by a banner
     * @param bannerColor color of the banner
     */
    public abstract void drawString(String str, double x, double y,
            int horizontal, int vertical, boolean framed, Color frameColor,
            double frameWidth, boolean banner, Color bannerColor);

    /**
     * Draws a TagString with a lot of parameters.
     * 
     * @param str Tagged text to be drawn
     * @param x coordinate to draw string
     * @param y coordinate to draw string
     * @param horizontal alignment of the text
     * @param vertical alignment of the text
     * @param framed true if text is surrounded by a frame
     * @param frameColor color of the frame
     * @param frameWidth witdh of the frame
     * @param banner true if the frame is filled by a banner
     * @param bannerColor color of the banner
     */
    public abstract void drawString(TagString str, double x, double y,
            int horizontal, int vertical, boolean framed, Color frameColor,
            double frameWidth, boolean banner, Color bannerColor);

    public abstract void endExport();

    public abstract void fillAndDraw(Shape s, Color fillColor);

    /**
     * Fills an arc. Uses Arc2D to call fill(Shape).
     * 
     */
    public abstract void fillArc(double x, double y, double width,
            double height, double startAngle, double arcAngle);

    /**
     * Fills an oval. Uses Ellipse2D to call fill(Shape).
     * 
     */
    public abstract void fillOval(double x, double y, double width,
            double height);

    /**
     * Fills a polygon. Uses createShape(...) to call fill(Shape).
     * 
     */
    public abstract void fillPolygon(double[] xPoints, double[] yPoints,
            int nPoints);

    /**
     * Fills a rectangle. Uses Rectangle2D to call fill(Shape).
     * 
     */
    public abstract void fillRect(double x, double y, double width,
            double height);

    /**
     * Fills a rounded rectangle. Uses RoundRectangle2D to call fill(Shape).
     * 
     */
    public abstract void fillRoundRect(double x, double y, double width,
            double height, double arcWidth, double arcHeight);

    public abstract int getColorMode();

    public abstract String getCreator();

    public abstract boolean isDeviceIndependent();

    public abstract void printComment(String comment);

    public abstract void setClip(double x, double y, double width, double height);

    public abstract void setColorMode(int colorMode);

    public abstract void setCreator(String creator);

    public abstract void setDeviceIndependent(boolean isDeviceIndependent);

    public abstract void setLineWidth(int width);

    public abstract void setLineWidth(double width);

    public abstract void startExport();

    // STATIC stuff below
    public static VectorGraphics create(Graphics g) {
        if ((g != null) && !(g instanceof VectorGraphics)) {
            return new PixelGraphics2D(g);
        }
        return (VectorGraphics) g;
    }

    // STATIC stuff below
    private static Hashtable<String, Integer> symbols = new Hashtable<String, Integer>(15);

    static {
        symbols.put("vline", new Integer(SYMBOL_VLINE));
        symbols.put("hline", new Integer(SYMBOL_HLINE));
        symbols.put("plus", new Integer(SYMBOL_PLUS));
        symbols.put("cross", new Integer(SYMBOL_CROSS));
        symbols.put("star", new Integer(SYMBOL_STAR));
        symbols.put("circle", new Integer(SYMBOL_CIRCLE));
        symbols.put("box", new Integer(SYMBOL_BOX));
        symbols.put("up_triangle", new Integer(SYMBOL_UP_TRIANGLE));
        symbols.put("dn_triangle", new Integer(SYMBOL_DN_TRIANGLE));
        symbols.put("diamond", new Integer(SYMBOL_DIAMOND));
    }

    private static Hashtable<String, Integer> alignments = new Hashtable<String, Integer>(6);

    static {
        alignments.put("baseline", new Integer(TEXT_BASELINE));
        alignments.put("left", new Integer(TEXT_LEFT));
        alignments.put("top", new Integer(TEXT_TOP));
        alignments.put("middle", new Integer(TEXT_CENTER));
        alignments.put("center", new Integer(TEXT_CENTER));
        alignments.put("right", new Integer(TEXT_RIGHT));
        alignments.put("bottom", new Integer(TEXT_BOTTOM));
    }

    public static int getTextAlignment(String name) {
        Integer i = alignments.get(name.toLowerCase());
        return (i != null) ? i.intValue() : TEXT_CENTER;
    }

    public static int getSymbol(String name) {
        Integer i = symbols.get(name.toLowerCase());
        return (i != null) ? i.intValue() : SYMBOL_PLUS;
    }

    public static double getYalignment(double y, double ascent, double descent,
            int alignment) {
        // vertical alignment
        switch (alignment) {
        case TEXT_TOP:
            y = y + ascent - descent;
            break;
        case TEXT_CENTER:
            y = y + ((ascent + descent) / 2) - descent;
            break;
        case TEXT_BOTTOM:
            y = y - descent;
            break;
        case TEXT_BASELINE:
        default:
            break;
        }
        return y;
    }

    public static double getXalignment(double x, double width, int alignment) {
        // horizontal alignment
        switch (alignment) {
        case TEXT_CENTER:
            x = x - (width / 2);
            break;
        case TEXT_RIGHT:
            x = x - width;
            break;
        case TEXT_LEFT:
        default:
            break;
        }
        return x;
    }
}
