/**
 * $RCSfile$
 * $Revision: $
 * $Date: $
 *
 * Copyright (C) 2008 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution, or a commercial license
 * agreement with Jive.
 */

package org.jivesoftware.openfire.sip.sipaccount;

import org.dom4j.Element;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.openfire.event.SessionEventListener;
import org.jivesoftware.openfire.session.Session;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;

import java.sql.SQLException;

/**
 * Implements an openfire Component and SessionEventListener. Controls every
 * registration inf request and process user's SIP presence informations.
 *
 * @author Thiago Rocha Camargo
 */
public class SipComponent implements Component, SessionEventListener {

    ComponentManager componentManager = null;

    /**
     * Namespace of the packet extension.
     */
    public static final String NAMESPACE = "http://www.jivesoftware.com/protocol/sipark";

    public static final String PROPNAME = "plugin.sipark.serviceName";

    public static final String NAME = "sipark";

    public SipComponent() {
        this.componentManager = ComponentManagerFactory.getComponentManager();
        SessionEventDispatcher.addListener(this);
    }

    public void initialize(JID jid, ComponentManager componentManager) {
    }

    public void start() {
    }

    public void shutdown() {
        SessionEventDispatcher.removeListener(this);
    }

    // Component Interface

    public void processPacket(Packet packet) {
        componentManager.getLog().debug(packet.toXML());
        if (packet instanceof IQ) {
            // Handle disco packets
            IQ iq = (IQ)packet;
            // Ignore IQs of type ERROR or RESULT
            if (IQ.Type.error == iq.getType() || IQ.Type.result == iq.getType()) {
                return;
            }
            processIQ(iq);
        }
    }

    private void processIQ(IQ iq) {
        IQ reply = IQ.createResultIQ(iq);
        String namespace = iq.getChildElement().getNamespaceURI();
        Element childElement = iq.getChildElement().createCopy();
        reply.setChildElement(childElement);

        if ("http://jabber.org/protocol/disco#info".equals(namespace)) {
            if (iq.getTo().getNode() == null) {
                // Return service identity and features
                Element identity = childElement.addElement("identity");
                identity.addAttribute("category", "component");
                identity.addAttribute("type", "generic");
                identity.addAttribute("name", "SIP Controller");
                childElement.addElement("feature").addAttribute("var", "http://jabber.org/protocol/disco#info");
                childElement.addElement("feature").addAttribute("var", "http://www.jivesoftware.com/protocol/sipark");

            }
        }
        else if (NAMESPACE.equals(namespace)) {
            if (iq.getTo().getNode() == null && iq.getFrom() != null) {

                SipAccount sipAccount = SipAccountDAO.getAccountByUser(iq.getFrom().toBareJID().split("@")[0]);

                if (iq.getChildElement().element("status") == null) {
                    if (sipAccount != null && sipAccount.isEnabled()) {
                        // There is a SIP mapping for this user so return mapping information
                        Element registration = childElement.addElement("registration");
                        registration.addElement("jid")
                                .setText(sipAccount.getUsername() + "@" + componentManager.getServerName());
                        registration.addElement("username").setText(sipAccount.getSipUsername());
                        registration.addElement("authUsername").setText(sipAccount.getAuthUsername());
                        registration.addElement("displayPhoneNum").setText(sipAccount.getDisplayName());
                        registration.addElement("password").setText(sipAccount.getPassword());
                        registration.addElement("server").setText(sipAccount.getServer());
                        registration.addElement("stunServer").setText(sipAccount.getStunServer());
                        registration.addElement("stunPort").setText(sipAccount.getStunPort());
                        registration.addElement("useStun").setText(String.valueOf(sipAccount.isUseStun()));
                        registration.addElement("voicemail").setText(sipAccount.getVoiceMailNumber());
                        registration.addElement("enabled").setText(String.valueOf(sipAccount.isEnabled()));
                        registration.addElement("outboundproxy").setText(sipAccount.getOutboundproxy());
                        registration.addElement("promptCredentials").setText(String.valueOf(sipAccount.isPromptCredentials()));
                    }
                    else {
                        // No SIP mapping was found
                        reply.getChildElement().addAttribute("type", "unregistered");
                    }
                }
                else {
                    if (sipAccount != null) {
                        Element status = iq.getChildElement().element("status");
                        if (!status.getTextTrim().equals("")) {
                            sipAccount.setStatus(SipRegisterStatus.valueOf(status.getTextTrim()));
                            try {
                                SipAccountDAO.update(sipAccount);
                            }
                            catch (SQLException e) {
                                componentManager.getLog().error(e);
                            }
                        }
                    }
                }
            }
        }
        else {
            // Answer an error since the server can't handle the requested
            // namespace
            reply.setError(PacketError.Condition.service_unavailable);
        }
        try {
            componentManager.sendPacket(this, reply);
        }
        catch (Exception e) {
            componentManager.getLog().error(e);
        }
        componentManager.getLog().debug("PACKET SENT: " + reply.toXML());
    } // Other Methods

    public String getDescription() {
        return "SIP Admin Plugin";
    }

    public String getName() {
        return "SIPAdmin";
    }

    public void anonymousSessionCreated(Session arg0) {
        // Ignore

    }

    public void anonymousSessionDestroyed(Session arg0) {
        // Ignore

    }

    public void sessionCreated(Session arg0) {
        // Ignore

    }

    /**
     * Sets the user SIP presence to Unregistered if his jabber account is disconnected.
     *
     * @param session the destroyed session
     */
    public void sessionDestroyed(Session session) {

        String username = session.getAddress().toBareJID().split("@")[0];

        SipAccount sipAccount = SipAccountDAO.getAccountByUser(username);
        if (sipAccount != null) {
            try {
                sipAccount.setStatus(SipRegisterStatus.Unregistered);
                SipAccountDAO.update(sipAccount);
            }
            catch (SQLException e) {
                componentManager.getLog().error(e);
            }
        }

    }

    public void resourceBound(Session session) {
    	// Do nothing.
    }
}
