/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/
package org.apache.batik.svggen;

import java.awt.Component;
import java.awt.Rectangle;

import javax.swing.AbstractButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JMenuBar;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollBar;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;

import org.w3c.dom.Element;
/**
 * This class offers a way to create an SVG document with grouping
 * that reflects the Swing composite structure (container/components).
 *
 * @author             Vincent Hardy
 * @version            1.1, May 2nd, 2000. Added
 */
public abstract class SwingSVGPrettyPrint implements SVGSyntax {
        /**
         * @param cmp Swing component to be converted to SVG
         * @param svgGen SVGraphics2D to use to paint Swing components
         * @return an SVG fragment containing an SVG equivalent of the Swing
         *         component tree.
         */
    public static void print(JComponent cmp, SVGGraphics2D svgGen) {
        if ((cmp instanceof JComboBox) || (cmp instanceof JScrollBar)) {
            // This is a work around unresolved issue with JComboBox
            // and JScrollBar
            printHack((JComponent)cmp, svgGen);
            return;
        }

        // Spawn a new Graphics2D for this component
        SVGGraphics2D g = (SVGGraphics2D)svgGen.create();
        g.setColor(cmp.getForeground());
        g.setFont(cmp.getFont());
        Element topLevelGroup = g.getTopLevelGroup();

        // If there is no area to be painted, return here
        if ((cmp.getWidth() <= 0) || (cmp.getHeight() <= 0))
            return;

        Rectangle clipRect = g.getClipBounds();
        if (clipRect == null)
            g.setClip(0, 0, cmp.getWidth(), cmp.getHeight());

        paintComponent(cmp, g);
        paintBorder(cmp, g);
        paintChildren(cmp, g);

        // Now, structure DOM tree to reflect this component's structure
        Element cmpGroup = g.getTopLevelGroup();
        cmpGroup.setAttributeNS(null, "id",
                                svgGen.getGeneratorContext().idGenerator.
                                generateID(cmp.getClass().getName()));

        topLevelGroup.appendChild(cmpGroup);
        svgGen.setTopLevelGroup(topLevelGroup);
    }

    /**
     * @param cmp Swing component to be converted to SVG
     * @param svgGen SVGraphics2D to use to paint Swing components
     * @return an SVG fragment containing an SVG equivalent of the Swing
     *         component tree.
     */
    private static void printHack(JComponent cmp, SVGGraphics2D svgGen) {
        // Spawn a new Graphics2D for this component
        SVGGraphics2D g = (SVGGraphics2D)svgGen.create();
        g.setColor(cmp.getForeground());
        g.setFont(cmp.getFont());
        Element topLevelGroup = g.getTopLevelGroup();

        // If there is no area to be painted, return here
        if ((cmp.getWidth() <= 0) || (cmp.getHeight() <= 0))
            return;

        Rectangle clipRect = g.getClipBounds();
        if (clipRect == null) {
            g.setClip(0, 0, cmp.getWidth(), cmp.getHeight());
        }

        cmp.paint(g);

        // Now, structure DOM tree to reflect this component's structure
        Element cmpGroup = g.getTopLevelGroup();
        cmpGroup.setAttributeNS(null, "id",
                                svgGen.getGeneratorContext().idGenerator.
                                generateID(cmp.getClass().getName()));

        topLevelGroup.appendChild(cmpGroup);
        svgGen.setTopLevelGroup(topLevelGroup);
    }


    private static void paintComponent(JComponent cmp, SVGGraphics2D svgGen){
        ComponentUI ui = UIManager.getUI(cmp);
        if(ui != null){
            ui.installUI(cmp);
            ui.update(svgGen, cmp);
        }
    }

    /**
     * WARNING: The following code does some special case processing
     * depending on the class of the input JComponent. This is needed
     * because there is no generic way I could find to determine whether
     * a component should be painted or not.
     */
    private static void paintBorder(JComponent cmp, SVGGraphics2D svgGen){
        Border border = cmp.getBorder();
        if(border != null){
            if( (cmp instanceof AbstractButton)
                ||
                (cmp instanceof JPopupMenu)
                ||
                (cmp instanceof JToolBar)
                ||
                (cmp instanceof JMenuBar)
                ||
                (cmp instanceof JProgressBar) ){
                if( ((cmp instanceof AbstractButton) && ((AbstractButton)cmp).isBorderPainted())
                    ||
                    ((cmp instanceof JPopupMenu) && ((JPopupMenu)cmp).isBorderPainted())
                    ||
                    ((cmp instanceof JToolBar) && ((JToolBar)cmp).isBorderPainted())
                    ||
                    ((cmp instanceof JMenuBar) && ((JMenuBar)cmp).isBorderPainted())
                    ||
                    ((cmp instanceof JProgressBar) && ((JProgressBar)cmp).isBorderPainted() ))
                    border.paintBorder(cmp, svgGen, 0, 0, cmp.getWidth(), cmp.getHeight());
            } else {
                border.paintBorder(cmp, svgGen, 0, 0, cmp.getWidth(), cmp.getHeight());
            }
        }
    }

    private static void paintChildren(JComponent cmp, SVGGraphics2D svgGen){
        int i = cmp.getComponentCount() - 1;
        boolean isJComponent = false;
        Rectangle tmpRect = new Rectangle();

        for(; i>=0; i--){
            Component comp = cmp.getComponent(i);

            if(comp != null && JComponent.isLightweightComponent(comp) &&
               (comp.isVisible() == true)) {
                Rectangle cr = null;
                isJComponent = (comp instanceof JComponent);

                if(isJComponent) {
                    cr = tmpRect;
                    ((JComponent)comp).getBounds(cr);
                } else {
                    cr = comp.getBounds();
                }

                Rectangle clip = svgGen.getClipBounds();
                Rectangle bounds = comp.getBounds(); // new Rectangle(cr);

                boolean hitClip =
                    svgGen.hitClip(cr.x, cr.y, cr.width, cr.height);

                if (hitClip) {
                    SVGGraphics2D cg = (SVGGraphics2D)svgGen.create(cr.x, cr.y, cr.width, cr.height);
                    cg.setColor(comp.getForeground());
                    cg.setFont(comp.getFont());
                    if(comp instanceof JComponent)
                        print((JComponent)comp, cg);
                    else{
                        comp.paint(cg);
                    }
                }
            }
        }
    }
}
