/*
 * Copyright 1999-2004 The Apache Software Foundation.
 * 
 * Licensed 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$ */

package org.apache.fop.svg;

import java.util.StringTokenizer;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.font.FontRenderContext;

import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;

import org.apache.batik.dom.svg.SVGDOMImplementation;

/**
 * Some utilities for creating svg DOM documents and elements.
 */
public class SVGUtilities {
    private static final String SVG_NS = SVGDOMImplementation.SVG_NAMESPACE_URI;

    /**
     * Create a new svg document with batik.
     * @param width the width of the root svg element
     * @param height the height of the root svg element
     * @return a new SVG Document
     */
    public static final Document createSVGDocument(float width,
            float height) {
        DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
        Document doc = impl.createDocument(SVG_NS, "svg", null);

        Element svgRoot = doc.getDocumentElement();
        svgRoot.setAttributeNS(null, "width", "" + width);
        svgRoot.setAttributeNS(null, "height", "" + height);
        return doc;
    }

    /**
     * Get the string width for a particular string given the font.
     * @param str the string
     * @param font the font
     * @return the width of the string in the given font
     */
    public static final float getStringWidth(String str, java.awt.Font font) {
        Rectangle2D rect =
            font.getStringBounds(str, 0, str.length(),
                                 new FontRenderContext(new AffineTransform(),
                                 true, true));
        return (float)rect.getWidth();
    }

    /**
     * Get the string height for a particular string given the font.
     * @param str the string
     * @param font the font
     * @return the height of the string in the given font
     */
    public static final float getStringHeight(String str,
                                              java.awt.Font font) {
        Rectangle2D rect =
            font.getStringBounds(str, 0, str.length(),
                                 new FontRenderContext(new AffineTransform(),
                                 true, true));
        return (float)rect.getHeight();
    }

    /**
     * Get the string bounds for a particular string given the font.
     * @param str the string
     * @param font the font
     * @return the bounds of the string
     */
    public static final Rectangle2D getStringBounds(String str,
            java.awt.Font font) {
        return font.getStringBounds(str, 0, str.length(),
                                    new FontRenderContext(new AffineTransform(),
                                    true, true));
    }

    /**
     * Create an SVG Line
     * @param doc the document to create the element
     * @param x the start x position
     * @param y the start y position
     * @param x2 the end x position
     * @param y2 the end y position
     * @return the new line element
     */
    public static final Element createLine(Document doc, float x, float y,
                                           float x2, float y2) {
        Element ellipse = doc.createElementNS(SVG_NS, "line");
        ellipse.setAttributeNS(null, "x1", "" + x);
        ellipse.setAttributeNS(null, "x2", "" + x2);
        ellipse.setAttributeNS(null, "y1", "" + y);
        ellipse.setAttributeNS(null, "y2", "" + y2);
        return ellipse;
    }

    /**
     * Create an SVG Ellipse
     * @param doc the document to create the element
     * @param cx the centre x position
     * @param cy the centre y position
     * @param rx the x axis radius
     * @param ry the y axis radius
     * @return the new ellipse element
     */
    public static final Element createEllipse(Document doc, float cx,
                                              float cy, float rx, float ry) {
        Element ellipse = doc.createElementNS(SVG_NS, "ellipse");
        ellipse.setAttributeNS(null, "cx", "" + cx);
        ellipse.setAttributeNS(null, "rx", "" + rx);
        ellipse.setAttributeNS(null, "cy", "" + cy);
        ellipse.setAttributeNS(null, "ry", "" + ry);
        return ellipse;
    }

    /**
     * Create an SVG Path.
     * @param doc the document to create the element
     * @param str the string for the d attribute on the path
     * @return the new path element
     */
    public static final Element createPath(Document doc, String str) {
        Element path = doc.createElementNS(SVG_NS, "path");
        path.setAttributeNS(null, "d", str);
        return path;
    }

    /**
     * Create an SVG Text object.
     * @param doc the document to create the element
     * @param x the start x position
     * @param y the start y position
     * @param str the string
     * @return the new text element
     */
    public static final Element createText(Document doc, float x, float y,
                                           String str) {
        Element textGraph = doc.createElementNS(SVG_NS, "text");
        textGraph.setAttributeNS(null, "x", "" + x);
        textGraph.setAttributeNS(null, "y", "" + y);
        org.w3c.dom.Text text = doc.createTextNode(str);
        textGraph.appendChild(text);
        return textGraph;
    }

    /**
     * Create an SVG Rectangle.
     * @param doc the document to create the element
     * @param x the start x position
     * @param y the start y position
     * @param width the width of the rectangle
     * @param height the height of the rectangle
     * @return the new rectangle element
     */
    public static final Element createRect(Document doc, float x, float y,
                                           float width, float height) {
        Element border = doc.createElementNS(SVG_NS, "rect");
        border.setAttributeNS(null, "x", "" + x);
        border.setAttributeNS(null, "y", "" + y);
        border.setAttributeNS(null, "width", "" + width);
        border.setAttributeNS(null, "height", "" + height);
        return border;
    }

    /**
     * Create an SVG G.
     * @param doc the document to create the element
     * @return the new g element
     */
    public static final Element createG(Document doc) {
        Element border = doc.createElementNS(SVG_NS, "g");
        return border;
    }

    /**
     * Create an SVG Clip.
     * @param doc the document to create the element
     * @param els the child elements that make the clip
     * @param id the id of the clipping path
     * @return the new clip element
     */
    public static final Element createClip(Document doc, Element els,
                                           String id) {
        Element border = doc.createElementNS(SVG_NS, "clipPath");
        border.setAttributeNS(null, "id", id);
        border.appendChild(els);
        return border;
    }

    /**
     * Create and svg image element.
     * @param doc the document to create the element
     * @param ref the href link to the image
     * @param width the width to set on the image
     * @param height the height to set on the image
     * @return a new image element
     */
    public static final Element createImage(Document doc, String ref,
                                            float width, float height) {
        Element border = doc.createElementNS(SVG_NS, "image");
        border.setAttributeNS("http://www.w3.org/1999/xlink", "href",
                              ref);
        border.setAttributeNS(null, "width", "" + width);
        border.setAttributeNS(null, "height", "" + height);
        return border;
    }

    /**
     * Create some SVG text that is wrapped into a specified width.
     * @param doc the document to create the elements
     * @param str the string to wrap
     * @param font the font
     * @param width the width to wrap
     * @return the new element containing the wrapped text
     */
    public static final Element wrapText(Document doc, String str,
                                         java.awt.Font font, float width) {
        Element g = createG(doc);
        Element text;
        StringTokenizer st = new StringTokenizer(str, " \t\r\n");
        float totalWidth = 0;
        String totalStr = "";
        int line = 0;
        float height = getStringHeight(str, font);
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            float strwidth = getStringWidth(token, font);
            totalWidth += strwidth;
            if (totalWidth > width) {
                if (totalStr.equals("")) {
                    totalStr = token;
                    token = "";
                    strwidth = 0;
                }
                text = createText(doc, 0, line * (height + 5), totalStr);
                g.appendChild(text);
                totalStr = token;
                totalWidth = strwidth;
                line++;
            } else {
                totalStr = totalStr + " " + token;
            }
        }

        return g;
    }

}
