/*

 ============================================================================
                   The Apache Software License, Version 1.1
 ============================================================================

 Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.

 Redistribution and use in source and binary forms, with or without modifica-
 tion, are permitted provided that the following conditions are met:

 1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

 3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

 4. The names "Batik" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    apache@apache.org.

 5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
 APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
 INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
 DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
 OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
 ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
 (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 This software  consists of voluntary contributions made  by many individuals
 on  behalf of the Apache Software  Foundation. For more  information on the
 Apache Software Foundation, please see <http://www.apache.org/>.

*/

package org.apache.batik.svggen;

import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

import java.util.Locale;

import org.w3c.dom.Document;

/**
 * This class contains all non graphical contextual information that
 * are needed by the {@link org.apache.batik.svggen.SVGGraphics2D} to
 * generate SVG from Java 2D primitives.
 * You can subclass it to change the defaults.
 *
 * @see org.apache.batik.svggen.SVGGraphics2D#SVGGraphics2D(SVGGeneratorContext,boolean)
 * @author <a href="mailto:cjolif@ilog.fr>Christophe Jolif</a>
 * @version $Id: SVGGeneratorContext.java,v 1.19 2003/11/23 20:54:24 deweese Exp $
 */
public class SVGGeneratorContext implements ErrorConstants {
    // this fields are package access for read-only purpose

    /**
     * Factory used by this Graphics2D to create Elements
     * that make the SVG DOM Tree
     */
    Document domFactory;

    /**
     * Handler that defines how images are referenced in the
     * generated SVG fragment. This allows different strategies
     * to be used to handle images.
     * @see org.apache.batik.svggen.ImageHandler
     * @see org.apache.batik.svggen.ImageHandlerBase64Encoder
     * @see org.apache.batik.svggen.ImageHandlerPNGEncoder
     * @see org.apache.batik.svggen.ImageHandlerJPEGEncoder
     */
    ImageHandler imageHandler;

    /**
     * Generic image handler. This allows more sophisticated 
     * image handling strategies than the <tt>ImageHandler</tt>
     * interfaces.
     */
    GenericImageHandler genericImageHandler;

    /**
     * To deal with Java 2D extension (custom java.awt.Paint for example).
     */
    ExtensionHandler extensionHandler;

    /**
     * To generate consitent ids.
     */
    SVGIDGenerator idGenerator;

    /**
     * To set style.
     */
    StyleHandler styleHandler;

    /**
     * The comment to insert at generation time.
     */
    String generatorComment;

    /**
     * The error handler.
     */
    ErrorHandler errorHandler;

    /**
     * Do we accept SVG Fonts generation?
     */
    boolean svgFont = false;

    /**
     * GraphicContextDefaults
     */
    GraphicContextDefaults gcDefaults;

    /**
     * Number of decimal places to use in output values.
     * 3 decimal places are used by default.
     */
    int precision;

    /**
     * Class to describe the GraphicContext defaults to
     * be used. Note that this class does *not* contain
     * a default for the initial transform, as this 
     * transform *has to be identity* for the SVGGraphics2D
     * to operate (the TransformStacks operation is based
     * on that assumption. See the DOMTreeManager class).
     */
    public static class GraphicContextDefaults {
        protected Paint paint;
        protected Stroke stroke;
        protected Composite composite;
        protected Shape clip;
        protected RenderingHints hints;
        protected Font font;
        protected Color background; 

        public void setStroke(Stroke stroke){
            this.stroke = stroke;
        }

        public Stroke getStroke(){
            return stroke;
        }

        public void setComposite(Composite composite){
            this.composite = composite;
        }

        public Composite getComposite(){
            return composite;
        }

        public void setClip(Shape clip){
            this.clip = clip;
        }

        public Shape getClip(){
            return clip;
        }

        public void setRenderingHints(RenderingHints hints){
            this.hints = hints;
        }

        public RenderingHints getRenderingHints(){
            return hints;
        }

        public void setFont(Font font){
            this.font = font;
        }

        public Font getFont(){
            return font;
        }

        public void setBackground(Color background){
            this.background = background;
        }

        public Color getBackground(){
            return background;
        }

        public void setPaint(Paint paint){
            this.paint = paint;
        }

        public Paint getPaint(){
            return paint;
        }
    }

    /**
     * Builds an instance of <code>SVGGeneratorContext</code> with the given
     * <code>domFactory</code> but let the user set later the other contextual
     * information. Please note that none of the parameter below should be
     * <code>null</code>.
     * @see #setIDGenerator
     * @see #setExtensionHandler
     * @see #setImageHandler
     * @see #setStyleHandler
     * @see #setErrorHandler
     */
    protected SVGGeneratorContext(Document domFactory) {
        setDOMFactory(domFactory);
    }

    /**
     * Creates an instance of <code>SVGGeneratorContext</code> with the
     * given <code>domFactory</code> and with the default values for the
     * other information.
     * @see org.apache.batik.svggen.SVGIDGenerator
     * @see org.apache.batik.svggen.DefaultExtensionHandler
     * @see org.apache.batik.svggen.ImageHandlerBase64Encoder
     * @see org.apache.batik.svggen.DefaultStyleHandler
     * @see org.apache.batik.svggen.DefaultErrorHandler
     */
    public static SVGGeneratorContext createDefault(Document domFactory) {
        SVGGeneratorContext ctx = new SVGGeneratorContext(domFactory);
        ctx.setIDGenerator(new SVGIDGenerator());
        ctx.setExtensionHandler(new DefaultExtensionHandler());
        ctx.setImageHandler(new ImageHandlerBase64Encoder());
        ctx.setStyleHandler(new DefaultStyleHandler());
        ctx.setComment("Generated by the Batik Graphics2D SVG Generator");
        ctx.setErrorHandler(new DefaultErrorHandler());
        return ctx;
    }

    /**
     * Returns the set of defaults which should be used for the 
     * GraphicContext.
     */
    final public GraphicContextDefaults getGraphicContextDefaults(){
        return gcDefaults;
    }

    /**
     * Sets the default to be used for the graphic context.
     * Note that gcDefaults may be null and that any of its attributes
     * may be null.
     */
    final public void setGraphicContextDefaults(GraphicContextDefaults gcDefaults){
        this.gcDefaults = gcDefaults;
    }

    /**
     * Returns the {@link org.apache.batik.svggen.SVGIDGenerator} that
     * has been set.
     */
    final public SVGIDGenerator getIDGenerator() {
        return idGenerator;
    }

    /**
     * Sets the {@link org.apache.batik.svggen.SVGIDGenerator}
     * to be used. It should not be <code>null</code>.
     */
    final public void setIDGenerator(SVGIDGenerator idGenerator) {
        if (idGenerator == null)
            throw new SVGGraphics2DRuntimeException(ERR_ID_GENERATOR_NULL);
        this.idGenerator = idGenerator;
    }

    /**
     * Returns the DOM Factory that
     * has been set.
     */
    final public Document getDOMFactory() {
        return domFactory;
    }

    /**
     * Sets the DOM Factory
     * to be used. It should not be <code>null</code>.
     */
    final public void setDOMFactory(Document domFactory) {
        if (domFactory == null)
            throw new SVGGraphics2DRuntimeException(ERR_DOM_FACTORY_NULL);
        this.domFactory = domFactory;
    }

    /**
     * Returns the {@link org.apache.batik.svggen.ExtensionHandler} that
     * has been set.
     */
    final public ExtensionHandler getExtensionHandler() {
        return extensionHandler;
    }

    /**
     * Sets the {@link org.apache.batik.svggen.ExtensionHandler}
     * to be used. It should not be <code>null</code>.
     */
    final public void setExtensionHandler(ExtensionHandler extensionHandler) {
        if (extensionHandler == null)
            throw new SVGGraphics2DRuntimeException(ERR_EXTENSION_HANDLER_NULL);
        this.extensionHandler = extensionHandler;
    }

    /**
     * Returns the {@link org.apache.batik.svggen.ImageHandler} that
     * has been set.
     */
    final public ImageHandler getImageHandler() {
        return imageHandler;
    }

    /**
     * Sets the {@link org.apache.batik.svggen.ImageHandler}
     * to be used. It should not be <code>null</code>.
     */
    final public void setImageHandler(ImageHandler imageHandler) {
        if (imageHandler == null)
            throw new SVGGraphics2DRuntimeException(ERR_IMAGE_HANDLER_NULL);
        this.imageHandler = imageHandler;
        this.genericImageHandler = new SimpleImageHandler(imageHandler);
    }

    /**
     * Sets the {@link org.apache.batik.svggen.GenericImageHandler}
     * to be used. 
     */
    final public void setGenericImageHandler(GenericImageHandler genericImageHandler){
        if (genericImageHandler == null){
            throw new SVGGraphics2DRuntimeException(ERR_IMAGE_HANDLER_NULL);
        }
        this.imageHandler = null;
        this.genericImageHandler = genericImageHandler;
    }

    /**
     * Returns the {@link org.apache.batik.svggen.StyleHandler} that
     * has been set.
     */
    final public StyleHandler getStyleHandler() {
        return styleHandler;
    }

    /**
     * Sets the {@link org.apache.batik.svggen.StyleHandler}
     * to be used. It should not be <code>null</code>.
     */
    final public void setStyleHandler(StyleHandler styleHandler) {
        if (styleHandler == null)
            throw new SVGGraphics2DRuntimeException(ERR_STYLE_HANDLER_NULL);
        this.styleHandler = styleHandler;
    }

    /**
     * Returns the comment to be generated in the SVG file.
     */
    final public String getComment() {
        return generatorComment;
    }

    /**
     * Sets the comment to be used. It can be <code>null</code> if you
     * want to disable it.
     */
    final public void setComment(String generatorComment) {
        this.generatorComment = generatorComment;
    }

    /**
     * Returns the {@link org.apache.batik.svggen.ErrorHandler} that
     * has been set.
     */
    final public ErrorHandler getErrorHandler() {
        return errorHandler;
    }

    /**
     * Sets the {@link org.apache.batik.svggen.ErrorHandler}
     * to be used. It should not be <code>null</code>.
     */
    final public void setErrorHandler(ErrorHandler errorHandler) {
        if (errorHandler == null)
            throw new SVGGraphics2DRuntimeException(ERR_ERROR_HANDLER_NULL);
        this.errorHandler = errorHandler;
    }

    /**
     * Returns <code>true</code> if we should generate SVG Fonts for
     * texts.
     */
    final public boolean isEmbeddedFontsOn() {
        return svgFont;
    }

    /**
     * Sets if we should generate SVG Fonts for texts. Default value
     * is <code>false</code>.
     */
    final public void setEmbeddedFontsOn(boolean svgFont) {
        this.svgFont = svgFont;
    }

    /**
     * Returns the current precision used by this context
     */
    final public int getPrecision() {
        return precision;
    }

    /**
     * Sets the precision used by this context. The precision controls
     * the number of decimal places used in floating point values
     * output by the SVGGraphics2D generator.
     * Note that the precision is clipped to the [0,12] range.
     */
    final public void setPrecision(int precision) {
        if (precision < 0) {
            this.precision = 0;
        } else if (precision > 12) {
            this.precision = 12;
        } else {
            this.precision = precision;
        }
        decimalFormat = decimalFormats[this.precision];
    }

    /**
     * Converts the input double value to a string with a number of 
     * decimal places controlled by the precision attribute.
     */
    final public String doubleString(double value) {
        double absvalue = Math.abs(value);
        // above 10e7 we do not output decimals as anyway
        // in scientific notation they were not available
        if (absvalue >= 10e7 || (int)value == value) {
            return Integer.toString((int)value);
        }
        // under 10e-3 we have to put decimals
        else {
            return decimalFormat.format(value);
        } 
    }

    /**
     * Current double value formatter
     */
    protected DecimalFormat decimalFormat = decimalFormats[3];

    protected static DecimalFormatSymbols dsf 
        = new DecimalFormatSymbols(Locale.US);

    protected static DecimalFormat decimalFormats[] = new DecimalFormat[13];

    static {
        decimalFormats[0] = new DecimalFormat("#", dsf);

        String format = "#.";
        for (int i=0; i<=12; i++) {
            format += "#";
            decimalFormats[i] = new DecimalFormat(format, dsf);
        }
    }

}
