/*
 * Jitsi Videobridge, OpenSource video conferencing.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jitsi.jicofo.openfire;

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

import org.slf4j.*;
import org.slf4j.Logger;

import org.jitsi.jicofo.*;
import org.jitsi.jicofo.osgi.*;
import org.jitsi.jicofo.xmpp.ConferenceIq;
import org.jitsi.jicofo.xmpp.ConferenceIqProvider;
import org.jitsi.jicofo.xmpp.IQUtils;
import org.jitsi.util.*;

import org.jitsi.videobridge.osgi.ServiceUtils2;

import org.jivesoftware.smack.packet.*;

import org.osgi.framework.*;
import org.xmpp.component.*;
import org.xmpp.packet.IQ;


public class FocusComponent extends AbstractComponent
{
    private static final Logger Log = LoggerFactory.getLogger(FocusComponent.class);

    /**
     * Indicates if the focus is anonymous user or authenticated system admin.
     */
    private final boolean isFocusAnonymous;

    /**
     * Optional password for focus user authentication. If authenticated login
     * is used we expect focus user to have admin privileges, so that it has
     * explicit moderator rights. Also in this case focus component will always
     * return 'ready=true' status, so that users don't have to wait for
     * the focus to create the room. If focus is authenticated and is not
     * an admin then will refuse to join MUC room.
     */

    /**
     * The manager object that creates and expires
     * {@link org.jitsi.jicofo.JitsiMeetConference}s.
     */
    private FocusManager focusManager;

    /**
     * Creates new instance of <tt>FocusComponent</tt>.
     * @param anonymousFocus indicates if the focus user is anonymous.
     */
    public FocusComponent(boolean anonymousFocus)
    {
        this.isFocusAnonymous = anonymousFocus;

        new ConferenceIqProvider();
        new ColibriIQProvider();
    }

    /**
     * Initializes this component.
     */
    public void init()
    {
		Log.info("Focus Manager init");
        this.focusManager = ServiceUtils.getService(FocusBundleActivator.bundleContext, FocusManager.class);
        focusManager.start();
    }

    /**
     * Releases resources used by this instance.
     */
    public void dispose()
    {
        focusManager.stop();
    }

    /**
     * Called as part of the execution of {@link AbstractComponent#shutdown()}
     * to enable this <tt>Component</tt> to finish cleaning resources up after
     * it gets completely shutdown.
     *
     * @see AbstractComponent#postComponentShutdown()
     */
    @Override
    public void postComponentShutdown()
    {
        super.postComponentShutdown();

        OSGi.stop();

        dispose();
    }

    /**
     * Called as part of the execution of {@link AbstractComponent#start()} to
     * enable this <tt>Component</tt> to finish initializing resources after it
     * gets completely started.
     *
     * @see AbstractComponent#postComponentStart()
     */
    @Override
    public void postComponentStart()
    {
        super.postComponentStart();

        FocusBundleActivator activator = new FocusBundleActivator()
		{
			@Override
			public void start(BundleContext bundleContext)  throws Exception
			{

			}

			@Override
			public void stop(BundleContext bundleContext) throws Exception
			{
				// TODO Auto-generated method stub
			}
		};

        OSGi.start(activator);

        init();
    }

    @Override
    public String getDescription()
    {
        return "Manages Jitsi Meet conferences";
    }

    @Override
    public String getName()
    {
        return "Jitsi Meet Focus";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String[] discoInfoFeatureNamespaces()
    {
        return
            new String[]
                {
                    ConferenceIq.NAMESPACE
                };
    }

    @Override
    protected IQ handleIQGet(IQ iq) throws Exception
    {
		Log.info("handleIQGet\n" + iq);
        try
        {
            org.jivesoftware.smack.packet.IQ smackIq = IQUtils.convert(iq);

            if (smackIq instanceof ColibriStatsIQ)
            {
				Log.info("handleIQGet ColibriStatsIQ " + smackIq);

                // Supports only empty colibri queries
                ColibriStatsIQ colibriStatsQuery
                    = (ColibriStatsIQ) smackIq;

                // Reply with stats
                ColibriStatsIQ statsReply = new ColibriStatsIQ();

                statsReply.setType(
                    org.jivesoftware.smack.packet.IQ.Type.RESULT);
                statsReply.setPacketID(iq.getID());
                statsReply.setTo(iq.getFrom().toString());

                int conferenceCount = focusManager.getConferenceCount();

                // Return conference count
                statsReply.addStat(
                    new ColibriStatsExtension.Stat(
                        "conferences",
                        Integer.toString(conferenceCount)));
                statsReply.addStat(
                    new ColibriStatsExtension.Stat(
                        "graceful_shutdown",
                        focusManager.isShutdownInProgress()
                                ? "true" : "false"));

                return IQUtils.convert(statsReply);
            }
            else
            {
                return super.handleIQGet(iq);
            }
        }
        catch (Exception e)
        {
            Log.error("handleIQGet", e);
            throw e;
        }
    }

    /**
     * Handles an <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>set</tt> which
     * represents a request.
     *
     * @param iq the <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>set</tt>
     * which represents the request to handle
     * @return an <tt>org.xmpp.packet.IQ</tt> stanza which represents the
     * response to the specified request or <tt>null</tt> to reply with
     * <tt>feature-not-implemented</tt>
     * @throws Exception to reply with <tt>internal-server-error</tt> to the
     * specified request
     * @see AbstractComponent#handleIQSet(IQ)
     */
    @Override
    public IQ handleIQSet(IQ iq)  throws Exception
    {
		Log.info("handleIQSet\n" + iq);

        try
        {
            org.jivesoftware.smack.packet.IQ smackIq = IQUtils.convert(iq);

            if (focusManager != null && smackIq instanceof ConferenceIq)
            {
                ConferenceIq query = (ConferenceIq) smackIq;
                ConferenceIq response = new ConferenceIq();
                String room = query.getRoom();

                Log.info("Focus request for room: " + room);

                if (focusManager.isShutdownInProgress() && focusManager.getConference(room) == null)
                {
                    // Service unavailable
                    org.jivesoftware.smack.packet.IQ smackReply  = ColibriConferenceIQ.createGracefulShutdownErrorResponse(query);
                    // Fix error responses
                    return IQUtils.convert(smackReply);
                }

                boolean ready = focusManager.conferenceRequest(room, query.getPropertiesMap());

                if (!isFocusAnonymous)
                {
                    // Focus is authenticated system admin, so we let
                    // them in immediately. Focus will get OWNER anyway.
                    ready = true;
                }

                response.setType(org.jivesoftware.smack.packet.IQ.Type.RESULT);
                response.setPacketID(query.getPacketID());
                response.setFrom(query.getTo());
                response.setTo(query.getFrom());
                response.setRoom(query.getRoom());
                response.setReady(ready);

                return IQUtils.convert(response);
            }
            else
            {
                return super.handleIQSet(iq);
            }
        }
        catch (Exception e)
        {
            Log.error("handleIQSet", e);
            throw e;
        }
    }
}
