/*

   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.

 */
package org.apache.batik.bridge.svg12;

import org.apache.batik.anim.dom.XBLEventSupport;
import org.apache.batik.bridge.Bridge;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.dom.AbstractNode;
import org.apache.batik.dom.events.EventSupport;
import org.apache.batik.dom.events.NodeEventTarget;
import org.apache.batik.dom.xbl.NodeXBL;
import org.apache.batik.constants.XMLConstants;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.MutationEvent;

/**
 * Bridge class for SVG 'text' elements with support for text content
 * that has been specified with XBL.
 *
 * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
 * @version $Id$
 */
public class SVG12TextElementBridge
        extends SVGTextElementBridge
        implements SVG12BridgeUpdateHandler {

    /**
     * Returns a new instance of this bridge.
     */
    public Bridge getInstance() {
        return new SVG12TextElementBridge();
    }

    /**
     * Adds the DOM listeners for this text bridge.
     */
    protected void addTextEventListeners(BridgeContext ctx, NodeEventTarget e) {
        if (childNodeRemovedEventListener == null) {
            childNodeRemovedEventListener =
                new DOMChildNodeRemovedEventListener();
        }
        if (subtreeModifiedEventListener == null) {
            subtreeModifiedEventListener =
                new DOMSubtreeModifiedEventListener();
        }

        SVG12BridgeContext ctx12 = (SVG12BridgeContext) ctx;
        AbstractNode n = (AbstractNode) e;
        XBLEventSupport evtSupport =
            (XBLEventSupport) n.initializeEventSupport();

        //to be notified when a child is removed from the 
        //<text> element.
        evtSupport.addImplementationEventListenerNS
            (XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMNodeRemoved",
             childNodeRemovedEventListener, true);
        ctx12.storeImplementationEventListenerNS
            (e, XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMNodeRemoved",
             childNodeRemovedEventListener, true);
        
        //to be notified when the modification of the subtree
        //of the <text> element is done
        evtSupport.addImplementationEventListenerNS
            (XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMSubtreeModified",
             subtreeModifiedEventListener, false);
        ctx12.storeImplementationEventListenerNS
            (e, XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMSubtreeModified",
             subtreeModifiedEventListener, false);
    }

    /**
     * Removes the DOM listeners for this text bridge.
     */
    protected void removeTextEventListeners(BridgeContext ctx,
                                            NodeEventTarget e) {
        AbstractNode n = (AbstractNode) e;
        XBLEventSupport evtSupport =
            (XBLEventSupport) n.initializeEventSupport();

        evtSupport.removeImplementationEventListenerNS
            (XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMNodeRemoved",
             childNodeRemovedEventListener, true);
        evtSupport.removeImplementationEventListenerNS
            (XMLConstants.XML_EVENTS_NAMESPACE_URI, "DOMSubtreeModified",
             subtreeModifiedEventListener, false);
    }

    /**
     * The DOM EventListener invoked when a node is removed.
     */
    protected class DOMChildNodeRemovedEventListener
            extends SVGTextElementBridge.DOMChildNodeRemovedEventListener {
        public void handleEvent(Event evt) {
            super.handleEvent(EventSupport.getUltimateOriginalEvent(evt));
        }
    }

    /**
     * The DOM EventListener invoked when the subtree is modified.
     */
    protected class DOMSubtreeModifiedEventListener
            extends SVGTextElementBridge.DOMSubtreeModifiedEventListener {
        public void handleEvent(Event evt) {
            super.handleEvent(EventSupport.getUltimateOriginalEvent(evt));
        }
    }

    // Tree navigation ------------------------------------------------------

    /**
     * Returns the first child node of the given node that should be
     * processed by the text bridge.
     */
    protected Node getFirstChild(Node n) {
        return ((NodeXBL) n).getXblFirstChild();
    }

    /**
     * Returns the next sibling node of the given node that should be
     * processed by the text bridge.
     */
    protected Node getNextSibling(Node n) {
        return ((NodeXBL) n).getXblNextSibling();
    }

    /**
     * Returns the parent node of the given node that should be
     * processed by the text bridge.
     */
    protected Node getParentNode(Node n) {
        return ((NodeXBL) n).getXblParentNode();
    }

    // SVG12BridgeUpdateHandler //////////////////////////////////////////////

    /**
     * Invoked when an MutationEvent of type 'DOMCharacterDataModified' 
     * is fired.
     */
    public void handleDOMCharacterDataModified(MutationEvent evt) {
        Node childNode = (Node)evt.getTarget();
        //if the parent is displayed, then discard the layout.
        if (isParentDisplayed(childNode)) {
            if (getParentNode(childNode) != childNode.getParentNode()) {
                // This text node was selected with an xbl:content element,
                // so a DOMSubtreeModified event is not going to be captured.
                // Better recompute the text layout now.
                computeLaidoutText(ctx, e, node);
            } else {
                laidoutText = null;
            }
        }
    }

    /**
     * Invoked when a bindable element's binding has changed.
     */
    public void handleBindingEvent(Element bindableElement,
                                   Element shadowTree) {
    }

    /**
     * Invoked when the xblChildNodes property has changed because a
     * descendant xbl:content element has updated its selected nodes.
     */
    public void handleContentSelectionChangedEvent
            (ContentSelectionChangedEvent csce) {
        computeLaidoutText(ctx, e, node);
    }
}
