/*
 * 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.jingle;

import net.java.sip.communicator.impl.protocol.jabber.extensions.*;

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

/**
 * An implementation of a Jingle IQ provider that parses incoming Jingle IQs.
 *
 * @author Emil Ivov
 */
public class JingleIQProvider implements IQProvider
{
    /**
     * Creates a new instance of the <tt>JingleIQProvider</tt> and register all
     * jingle related extension providers. It is the responsibility of the
     * application to register the <tt>JingleIQProvider</tt> itself.
     */
    public JingleIQProvider()
    {

        AbstractSmackInteroperabilityLayer smackInteroperabilityLayer = 
                AbstractSmackInteroperabilityLayer.getInstance();

        //<description/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                RtpDescriptionPacketExtension.ELEMENT_NAME,
                RtpDescriptionPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <RtpDescriptionPacketExtension>(
                        RtpDescriptionPacketExtension.class));

        //<payload-type/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                PayloadTypePacketExtension.ELEMENT_NAME,
                RtpDescriptionPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <PayloadTypePacketExtension>(
                        PayloadTypePacketExtension.class));

        //<parameter/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                ParameterPacketExtension.ELEMENT_NAME,
                RtpDescriptionPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <ParameterPacketExtension>
                        (ParameterPacketExtension.class));

        //<rtp-hdrext/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                RTPHdrExtPacketExtension.ELEMENT_NAME,
                RTPHdrExtPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <RTPHdrExtPacketExtension>
                        (RTPHdrExtPacketExtension.class));

        // <sctpmap/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                SctpMapExtension.ELEMENT_NAME,
                SctpMapExtension.NAMESPACE,
                new SctpMapExtensionProvider());

        //<encryption/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                EncryptionPacketExtension.ELEMENT_NAME,
                RtpDescriptionPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <EncryptionPacketExtension>
                        (EncryptionPacketExtension.class));

        //<zrtp-hash/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                ZrtpHashPacketExtension.ELEMENT_NAME,
                ZrtpHashPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <ZrtpHashPacketExtension>
                        (ZrtpHashPacketExtension.class));

        //<crypto/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                CryptoPacketExtension.ELEMENT_NAME,
                RtpDescriptionPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <CryptoPacketExtension>
                        (CryptoPacketExtension.class));

        // <bundle/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                BundlePacketExtension.ELEMENT_NAME,
                BundlePacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <BundlePacketExtension>
                        (BundlePacketExtension.class));

        // <group/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                GroupPacketExtension.ELEMENT_NAME,
                GroupPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <GroupPacketExtension>(GroupPacketExtension.class));

        //ice-udp transport
        smackInteroperabilityLayer.addExtensionProvider(
                IceUdpTransportPacketExtension.ELEMENT_NAME,
                IceUdpTransportPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <IceUdpTransportPacketExtension>(
                        IceUdpTransportPacketExtension.class));

        //<raw-udp/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                RawUdpTransportPacketExtension.ELEMENT_NAME,
                RawUdpTransportPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <RawUdpTransportPacketExtension>(
                        RawUdpTransportPacketExtension.class));

        //ice-udp <candidate/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                CandidatePacketExtension.ELEMENT_NAME,
                IceUdpTransportPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <CandidatePacketExtension>(
                        CandidatePacketExtension.class));

        //raw-udp <candidate/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                CandidatePacketExtension.ELEMENT_NAME,
                RawUdpTransportPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <CandidatePacketExtension>(
                        CandidatePacketExtension.class));

        //ice-udp <remote-candidate/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                RemoteCandidatePacketExtension.ELEMENT_NAME,
                IceUdpTransportPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <RemoteCandidatePacketExtension>(
                        RemoteCandidatePacketExtension.class));

        //inputevt <inputevt/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                InputEvtPacketExtension.ELEMENT_NAME,
                InputEvtPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<InputEvtPacketExtension>(
                        InputEvtPacketExtension.class));

        //coin <conference-info/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                CoinPacketExtension.ELEMENT_NAME,
                CoinPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<CoinPacketExtension>(
                        CoinPacketExtension.class));

        // DTLS-SRTP
        smackInteroperabilityLayer.addExtensionProvider(
                DtlsFingerprintPacketExtension.ELEMENT_NAME,
                DtlsFingerprintPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider
                        <DtlsFingerprintPacketExtension>(
                        DtlsFingerprintPacketExtension.class));

        /*
         * XEP-0251: Jingle Session Transfer <transfer/> and <transferred>
         * providers
         */
        smackInteroperabilityLayer.addExtensionProvider(
                TransferPacketExtension.ELEMENT_NAME,
                TransferPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<TransferPacketExtension>(
                        TransferPacketExtension.class));
        smackInteroperabilityLayer.addExtensionProvider(
                TransferredPacketExtension.ELEMENT_NAME,
                TransferredPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<TransferredPacketExtension>(
                        TransferredPacketExtension.class));

        //conference description <callid/> provider
        smackInteroperabilityLayer.addExtensionProvider(
                ConferenceDescriptionPacketExtension.CALLID_ELEM_NAME,
                ConferenceDescriptionPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<CallIdPacketExtension>(
                        CallIdPacketExtension.class));

        //rtcp-fb
        smackInteroperabilityLayer.addExtensionProvider(
                RtcpFbPacketExtension.ELEMENT_NAME,
                RtcpFbPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<RtcpFbPacketExtension>(
                        RtcpFbPacketExtension.class));

        //rtcp-mux
        smackInteroperabilityLayer.addExtensionProvider(
                RtcpmuxPacketExtension.ELEMENT_NAME,
                IceUdpTransportPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<RtcpmuxPacketExtension>(
                        RtcpmuxPacketExtension.class));

        //ssrcInfo
        smackInteroperabilityLayer.addExtensionProvider(
                SSRCInfoPacketExtension.ELEMENT_NAME,
                SSRCInfoPacketExtension.NAMESPACE,
                new DefaultPacketExtensionProvider<SSRCInfoPacketExtension>(
                        SSRCInfoPacketExtension.class));
    }

    /**
     * Parses a Jingle IQ sub-document and returns a {@link JingleIQ} instance.
     *
     * @param parser an XML parser.
     *
     * @return a new {@link JingleIQ} instance.
     *
     * @throws Exception if an error occurs parsing the XML.
     */
    public JingleIQ parseIQ(XmlPullParser parser)
        throws Exception
    {
        JingleIQ jingleIQ = new JingleIQ();

        //let's first handle the "jingle" element params.
        JingleAction action = JingleAction.parseString(parser
                        .getAttributeValue("", JingleIQ.ACTION_ATTR_NAME));
        String initiator = parser
                         .getAttributeValue("", JingleIQ.INITIATOR_ATTR_NAME);
        String responder = parser
                        .getAttributeValue("", JingleIQ.RESPONDER_ATTR_NAME);
        String sid = parser
                        .getAttributeValue("", JingleIQ.SID_ATTR_NAME);

        jingleIQ.setAction(action);
        jingleIQ.setInitiator(initiator);
        jingleIQ.setResponder(responder);
        jingleIQ.setSID(sid);

        boolean done = false;

        // Sub-elements providers
        DefaultPacketExtensionProvider<ContentPacketExtension> contentProvider
            = new DefaultPacketExtensionProvider<ContentPacketExtension>(
                    ContentPacketExtension.class);
        ReasonProvider reasonProvider = new ReasonProvider();
        DefaultPacketExtensionProvider<TransferPacketExtension> transferProvider
            = new DefaultPacketExtensionProvider<TransferPacketExtension>(
                    TransferPacketExtension.class);
        DefaultPacketExtensionProvider<CoinPacketExtension> coinProvider
            = new DefaultPacketExtensionProvider<CoinPacketExtension>(
                    CoinPacketExtension.class);
        DefaultPacketExtensionProvider<CallIdPacketExtension> callidProvider
            = new DefaultPacketExtensionProvider<CallIdPacketExtension>(
                    CallIdPacketExtension.class);

        // Now go on and parse the jingle element's content.
        int eventType;
        String elementName;
        String namespace;

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

            if (eventType == XmlPullParser.START_TAG)
            {
                // <content/>
                if (elementName.equals(ContentPacketExtension.ELEMENT_NAME))
                {
                    ContentPacketExtension content
                        = contentProvider.parseExtension(parser);
                    jingleIQ.addContent(content);
                }
                // <reason/>
                else if(elementName.equals(ReasonPacketExtension.ELEMENT_NAME))
                {
                    ReasonPacketExtension reason
                        = reasonProvider.parseExtension(parser);
                    jingleIQ.setReason(reason);
                }
                // <transfer/>
                else if (elementName.equals(
                                TransferPacketExtension.ELEMENT_NAME)
                        && namespace.equals(TransferPacketExtension.NAMESPACE))
                {
                    jingleIQ.addExtension(
                            transferProvider.parseExtension(parser));
                }
                else if(elementName.equals(CoinPacketExtension.ELEMENT_NAME))
                {
                    jingleIQ.addExtension(coinProvider.parseExtension(parser));
                }
                else if (elementName.equals(
                        ConferenceDescriptionPacketExtension.CALLID_ELEM_NAME))
                {
                    jingleIQ.addExtension(callidProvider.parseExtension(parser));
                }

                //<mute/> <active/> and other session-info elements
                if (namespace.equals( SessionInfoPacketExtension.NAMESPACE))
                {
                    SessionInfoType type = SessionInfoType.valueOf(elementName);

                    //<mute/>
                    if( type == SessionInfoType.mute
                        || type == SessionInfoType.unmute)
                    {
                        String name = parser.getAttributeValue("",
                                MuteSessionInfoPacketExtension.NAME_ATTR_VALUE);

                        jingleIQ.setSessionInfo(
                                new MuteSessionInfoPacketExtension(
                                        type == SessionInfoType.mute, name));
                    }
                    //<hold/>, <unhold/>, <active/>, etc.
                    else
                    {
                        jingleIQ.setSessionInfo(
                                        new SessionInfoPacketExtension(type));
                    }
                }
            }

            if ((eventType == XmlPullParser.END_TAG)
                    && parser.getName().equals(JingleIQ.ELEMENT_NAME))
            {
                    done = true;
            }
        }
        return jingleIQ;
    }
}
