/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: ImageConverterSVG2G2D.java 1664570 2015-03-06 09:41:07Z lbernardo $ */

package org.apache.fop.image.loader.batik;

import java.awt.Dimension;
import java.awt.geom.AffineTransform;
import java.util.Map;

import org.w3c.dom.Document;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DefaultFontFamilyResolver;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.gvt.GraphicsNode;

import org.apache.xmlgraphics.image.GraphicsConstants;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.image.loader.XMLNamespaceEnabledImageFlavor;
import org.apache.xmlgraphics.image.loader.impl.AbstractImageConverter;
import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
import org.apache.xmlgraphics.util.UnitConv;

import org.apache.fop.svg.SimpleSVGUserAgent;

/**
 * This ImageConverter converts SVG images to Java2D.
 * <p>
 * Note: The target flavor is "generic" Java2D. No Batik-specific bridges are hooked into the
 * conversion process. Specialized renderers may want to provide specialized adapters to profit
 * from target-format features (for example with PDF or PS). This converter is mainly for formats
 * which only support bitmap images or rudimentary Java2D support.
 */
public class ImageConverterSVG2G2D extends AbstractImageConverter {

    /** logger */
    private static Log log = LogFactory.getLog(ImageConverterSVG2G2D.class);

    /** {@inheritDoc} */
    public Image convert(final Image src, Map hints) throws ImageException {
        checkSourceFlavor(src);
        final ImageXMLDOM svg = (ImageXMLDOM)src;
        if (!SVGDOMImplementation.SVG_NAMESPACE_URI.equals(svg.getRootNamespace())) {
            throw new IllegalArgumentException("XML DOM is not in the SVG namespace: "
                    + svg.getRootNamespace());
        }

        //Prepare
        float pxToMillimeter = UnitConv.IN2MM / GraphicsConstants.DEFAULT_DPI;
        Number ptm = (Number)hints.get(ImageProcessingHints.SOURCE_RESOLUTION);
        if (ptm != null) {
            pxToMillimeter = (float)(UnitConv.IN2MM / ptm.doubleValue());
        }
        UserAgent ua = createBatikUserAgent(pxToMillimeter);
        GVTBuilder builder = new GVTBuilder();

        final ImageManager imageManager = (ImageManager)hints.get(
                ImageProcessingHints.IMAGE_MANAGER);
        final ImageSessionContext sessionContext = (ImageSessionContext)hints.get(
                ImageProcessingHints.IMAGE_SESSION_CONTEXT);

        boolean useEnhancedBridgeContext = (imageManager != null && sessionContext != null);
        final BridgeContext ctx = (useEnhancedBridgeContext
                ? new GenericFOPBridgeContext(ua, null, imageManager, sessionContext)
                : new BridgeContext(ua));

        Document doc = svg.getDocument();
        //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
        //to it.
        Document clonedDoc = BatikUtil.cloneSVGDocument(doc);

        //Build the GVT tree
        final GraphicsNode root;
        try {
            root = builder.build(ctx, clonedDoc);
        } catch (Exception e) {
            throw new ImageException("GVT tree could not be built for SVG graphic", e);
        }

        //Create the painter
        int width = svg.getSize().getWidthMpt();
        int height = svg.getSize().getHeightMpt();
        Dimension imageSize = new Dimension(width, height);
        Graphics2DImagePainter painter = createPainter(ctx, root, imageSize);

        //Create g2d image
        ImageInfo imageInfo = src.getInfo();
        ImageGraphics2D g2dImage = new ImageGraphics2D(imageInfo, painter);
        return g2dImage;
    }

    /**
     * Creates a user agent for Batik. Override to provide your own user agent.
     * @param pxToMillimeter the source resolution (in px per millimeter)
     * @return the newly created user agent
     */
    protected SimpleSVGUserAgent createBatikUserAgent(float pxToMillimeter) {
        return new SimpleSVGUserAgent(pxToMillimeter, new AffineTransform(),
                DefaultFontFamilyResolver.SINGLETON) {
            /** {@inheritDoc} */
            public void displayMessage(String message) {
                //TODO Refine and pipe through to caller
                log.info(message);
            }

            /** {@inheritDoc} */
            public void displayError(Exception e) {
                log.error("Error converting SVG to a Java2D graphic", e);
            }

            /** {@inheritDoc} */
            public void displayError(String message) {
                log.error(message);
            }


        };
    }

    /**
     * Creates a Graphics 2D image painter
     *
     * @param ctx the bridge context
     * @param root the graphics node root
     * @param imageSize the image size
     * @return the newly created graphics 2d image painter
     */
    protected Graphics2DImagePainter createPainter(
            BridgeContext ctx, GraphicsNode root, Dimension imageSize) {
        return new Graphics2DImagePainterImpl(root, ctx, imageSize);
    }

    /** {@inheritDoc} */
    public ImageFlavor getSourceFlavor() {
        return XMLNamespaceEnabledImageFlavor.SVG_DOM;
    }

    /** {@inheritDoc} */
    public ImageFlavor getTargetFlavor() {
        return ImageFlavor.GRAPHICS2D;
    }

}
