/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * 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.
 */
package net.java.sip.communicator.impl.protocol.jabber.extensions;

import java.util.logging.*;

import net.java.sip.communicator.service.protocol.jabber.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;

/**
 * A provider that parses incoming packet extensions into instances of the
 * {@link Class} that it has been instantiated for.
 *
 * @param <C> Class that the packets we will be parsing belong to
 * @author Emil Ivov
 */
public class DefaultPacketExtensionProvider<C extends AbstractPacketExtension>
    implements PacketExtensionProvider
{
    /**
     * The <tt>Logger</tt> used by the <tt>DefaultPacketExtensionProvider</tt>
     * class and its instances for logging output.
     */
    private static final Logger logger = Logger
                    .getLogger(DefaultPacketExtensionProvider.class.getName());

    /**
     * The <tt>AbstractSmackInteroperabilityLayer</tt> instance implementing
     * necessary methods
     */
    private AbstractSmackInteroperabilityLayer smackInteroperabilityLayer = 
            AbstractSmackInteroperabilityLayer.getInstance();
    
    /**
     * The {@link Class} that the packets we will be parsing here belong to.
     */
    private final Class<C> packetClass;

    /**
     * Creates a new packet provider for the specified packet extensions.
     *
     * @param c the {@link Class} that the packets we will be parsing belong to.
     */
    public DefaultPacketExtensionProvider(Class<C> c)
    {
        this.packetClass = c;
    }

    /**
     * Parse an extension sub-packet and create a <tt>C</tt> instance. At
     * the beginning of the method call, the xml parser will be positioned on
     * the opening element of the packet extension and at the end of the method
     * call it will be on the closing element of the packet extension.
     *
     * @param parser an XML parser positioned at the packet's starting element.
     *
     * @return a new packet extension instance.
     *
     * @throws java.lang.Exception if an error occurs parsing the XML.
     */
    public C parseExtension(XmlPullParser parser) throws Exception
    {
        C packetExtension = packetClass.newInstance();

        //first, set all attributes
        int attrCount = parser.getAttributeCount();

        for (int i = 0; i < attrCount; i++)
        {
            packetExtension.setAttribute(
                            parser.getAttributeName(i),
                            parser.getAttributeValue(i));
        }

        //now parse the sub elements
        boolean done = false;
        int eventType;
        String elementName;
        String namespace;

        while (!done)
        {
            eventType = parser.next();
            elementName = parser.getName();
            namespace = parser.getNamespace();

            if (logger.isLoggable(Level.FINEST))
                logger.finest("Will parse " + elementName
                    + " ns=" + namespace
                    + " class=" + packetExtension.getClass().getSimpleName());

            if (eventType == XmlPullParser.START_TAG)
            {
                PacketExtensionProvider provider = smackInteroperabilityLayer
                        .getExtensionProvider( elementName, namespace );

                if(provider == null)
                {
                    //we don't know how to handle this kind of extensions.
                    logger.fine("Could not add a provider for element "
                        + elementName + " from namespace " + namespace);
                }
                else
                {
                    PacketExtension childExtension
                        = provider.parseExtension(parser);

                    if(namespace != null)
                    {
                        if(childExtension instanceof AbstractPacketExtension)
                        {
                            ((AbstractPacketExtension)childExtension).
                                setNamespace(namespace);
                        }
                    }
                    packetExtension.addChildExtension(childExtension);
                }
            }
            if (eventType == XmlPullParser.END_TAG)
            {
                if (parser.getName().equals(packetExtension.getElementName()))
                {
                    done = true;
                }
            }
            if (eventType == XmlPullParser.TEXT)
            {
                String text = parser.getText();
                packetExtension.setText(text);
            }

            if (logger.isLoggable(Level.FINEST))
                logger.finest("Done parsing " + elementName);
        }

        return packetExtension;
    }
}
