/**
 * $RCSfile$
 * $Revision: 7071 $
 * $Date: 2007-02-12 08:59:05 +0800 (Mon, 12 Feb 2007) $
 *
 * Copyright 2003-2007 Jive Software.
 *
 * All rights reserved. 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 org.jivesoftware.smackx.packet;

import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * A DiscoverInfo IQ packet, which is used by XMPP clients to request and receive information 
 * to/from other XMPP entities.<p> 
 * 
 * The received information may contain one or more identities of the requested XMPP entity, and 
 * a list of supported features by the requested XMPP entity.
 *
 * @author Gaston Dombiak
 */
public class DiscoverInfo extends IQ {

    private final List<Feature> features = new CopyOnWriteArrayList<Feature>();
    private final List<Identity> identities = new CopyOnWriteArrayList<Identity>();
    private String node;

    /**
     * Adds a new feature to the discovered information.
     *
     * @param feature the discovered feature
     */
    public void addFeature(String feature) {
        addFeature(new Feature(feature));
    }

    private void addFeature(Feature feature) {
        features.add(feature);
    }

    /**
     * Returns the discovered features of an XMPP entity.
     *
     * @return an Iterator on the discovered features of an XMPP entity
     */
    public Iterator<Feature> getFeatures() {
        return Collections.unmodifiableList(features).iterator();
    }

    /**
     * Adds a new identity of the requested entity to the discovered information.
     * 
     * @param identity the discovered entity's identity
     */
    public void addIdentity(Identity identity) {
        identities.add(identity);
    }

    /**
     * Returns the discovered identities of an XMPP entity.
     * 
     * @return an Iterator on the discoveted identities 
     */
    public Iterator<Identity> getIdentities() {
        return Collections.unmodifiableList(identities).iterator();
    }

    /**
     * Returns the node attribute that supplements the 'jid' attribute. A node is merely 
     * something that is associated with a JID and for which the JID can provide information.<p> 
     * 
     * Node attributes SHOULD be used only when trying to provide or query information which 
     * is not directly addressable.
     *
     * @return the node attribute that supplements the 'jid' attribute
     */
    public String getNode() {
        return node;
    }

    /**
     * Sets the node attribute that supplements the 'jid' attribute. A node is merely 
     * something that is associated with a JID and for which the JID can provide information.<p> 
     * 
     * Node attributes SHOULD be used only when trying to provide or query information which 
     * is not directly addressable.
     * 
     * @param node the node attribute that supplements the 'jid' attribute
     */
    public void setNode(String node) {
        this.node = node;
    }

    /**
     * Returns true if the specified feature is part of the discovered information.
     * 
     * @param feature the feature to check
     * @return true if the requestes feature has been discovered
     */
    public boolean containsFeature(String feature) {
        for (Iterator<Feature> it = getFeatures(); it.hasNext();) {
            if (feature.equals(it.next().getVar()))
                return true;
        }
        return false;
    }

    public String getChildElementXML() {
        StringBuilder buf = new StringBuilder();
        buf.append("<query xmlns=\"http://jabber.org/protocol/disco#info\"");
        if (getNode() != null) {
            buf.append(" node=\"");
            buf.append(getNode());
            buf.append("\"");
        }
        buf.append(">");
        for (Identity identity : identities) {
            buf.append(identity.toXML());
        }
        for (Feature feature : features) {
            buf.append(feature.toXML());
        }
        // Add packet extensions, if any are defined.
        buf.append(getExtensionsXML());
        buf.append("</query>");
        return buf.toString();
    }

    public DiscoverInfo clone() {
        DiscoverInfo d = new DiscoverInfo();

        // Set node
        d.setNode(getNode());

        // Copy features
        for (Feature f : features) {
            d.addFeature(f);
        }

        // Copy identities
        for (Identity i : identities) {
            d.addIdentity(i);
        }
        
        // Copy extensions
        for (PacketExtension pe : getExtensions()) {
            d.addExtension(pe);
        }

        return d;
    }

    /**
     * Represents the identity of a given XMPP entity. An entity may have many identities but all
     * the identities SHOULD have the same name.<p>
     * 
     * Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
     * in order to get the official registry of values for the <i>category</i> and <i>type</i> 
     * attributes.
     * 
     */
    public static class Identity {

        private String category;
        private String name;
        private String language;
        private String type;

        /**
         * Creates a new identity for an XMPP entity.
         * 
         * @param category the entity's category.
         * @param name the entity's name.
         */
        public Identity(String category, String name) {
            this.category = category;
            this.name = name;
        }

        /**
         * Returns the entity's category. To get the official registry of values for the 
         * 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> 
         *
         * @return the entity's category.
         */
        public String getCategory() {
            return category;
        }

        /**
         * Returns the identity's name.
         *
         * @return the identity's name.
         */
        public String getName() {
            return name;
        }

        /**
         * Returns the entity's type. To get the official registry of values for the 
         * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> 
         *
         * @return the entity's type.
         */
        public String getType() {
            return type;
        }

        /**
         * Sets the entity's type. To get the official registry of values for the 
         * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> 
         *
         * @param type the identity's type.
         */
        public void setType(String type) {
            this.type = type;
        }

        /**
         * Returns the name's language. 
         *
         * @return the name's language.
         */
        public String getLanguage() {
            return language;
        }

        /**
         * Sets the name's language. 
         *
         * @param type the name's language.
         */
        public void setLanguage(String language) {
            this.language = language;
        }

        public String toXML() {
            StringBuilder buf = new StringBuilder();
            buf.append("<identity category=\"").append(category).append("\"");
            buf.append(" name=\"").append(name).append("\"");
            if (type != null) {
                buf.append(" type=\"").append(type).append("\"");
            }
            if (language != null) {
                buf.append(" xml:lang=\"").append(language).append("\"");
            }
            buf.append("/>");
            return buf.toString();
        }
    }

    /**
     * Represents the features offered by the item. This information helps requestors determine 
     * what actions are possible with regard to this item (registration, search, join, etc.) 
     * as well as specific feature types of interest, if any (e.g., for the purpose of feature 
     * negotiation).
     */
    public static class Feature {

        private String variable;

        /**
         * Creates a new feature offered by an XMPP entity or item.
         * 
         * @param variable the feature's variable.
         */
        public Feature(String variable) {
            this.variable = variable;
        }

        /**
         * Returns the feature's variable.
         *
         * @return the feature's variable.
         */
        public String getVar() {
            return variable;
        }

        public String toXML() {
            StringBuilder buf = new StringBuilder();
            buf.append("<feature var=\"").append(variable).append("\"/>");
            return buf.toString();
        }
    }
}
