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

import java.util.*;

import net.java.sip.communicator.service.protocol.event.*;

import org.osgi.framework.*;

/**
 * Provides utilities to aid the manipulation of {@link AccountManager}.
 *
 * @author Lyubomir Marinov
 */
public final class AccountManagerUtils
{
    private static AccountManager getAccountManager(BundleContext bundleContext)
    {
        return
            bundleContext.getService(
                    bundleContext.getServiceReference(AccountManager.class));
    }

    /**
     * Starts a specific <code>Bundle</code> and waits for the
     * <code>AccountManager</code> available in a specific
     * <code>BundleContext</code> to load the stored accounts of a
     * <code>ProtocolProviderFactory</code> with a specific protocol name.
     *
     * @param bundleContextWithAccountManager
     *            the <code>BundleContext</code> in which an
     *            <code>AccountManager</code> service is registered
     * @param bundleToStart
     *            the <code>Bundle</code> to be started
     * @param protocolNameToWait
     *            the protocol name of a <code>ProtocolProviderFactory</code> to
     *            wait the end of the loading of the stored accounts for
     * @throws BundleException
     * @throws InterruptedException
     *             if any thread interrupted the current thread before or while
     *             the current thread was waiting for the loading of the stored
     *             accounts
     */
    public static void startBundleAndWaitStoredAccountsLoaded(
            BundleContext bundleContextWithAccountManager,
            final Bundle bundleToStart,
            final String protocolNameToWait)
        throws BundleException,
               InterruptedException
    {
        AccountManager accountManager
            = getAccountManager(bundleContextWithAccountManager);
        final boolean[] storedAccountsAreLoaded = new boolean[1];
        AccountManagerListener listener = new AccountManagerListener()
        {
            public void handleAccountManagerEvent(AccountManagerEvent event)
            {
                if (AccountManagerEvent.STORED_ACCOUNTS_LOADED
                        != event.getType())
                    return;

                ProtocolProviderFactory factory = event.getFactory();

                /*
                 * If the event is for a factory with a protocol name other than
                 * protocolNameToWait, it's not the one we're waiting for.
                 */
                if ((factory != null)
                        && !protocolNameToWait
                                .equals(factory.getProtocolName()))
                    return;

                /*
                 * If the event if for a factory which is no longer registered,
                 * then it's not the one we're waiting for because we're waiting
                 * for the specified bundle to start and register a factory.
                 */
                if (factory != null)
                {
                    BundleContext bundleContext
                        = bundleToStart.getBundleContext();

                    /*
                     * If the specified bundle still hasn't started, the event
                     * cannot be the one we're waiting for.
                     */
                    if (bundleContext == null)
                        return;

                    Collection<ServiceReference<ProtocolProviderFactory>> factoryRefs;

                    try
                    {
                        factoryRefs
                            = bundleContext.getServiceReferences(
                                    ProtocolProviderFactory.class,
                                    "("
                                        + ProtocolProviderFactory.PROTOCOL
                                        + "="
                                        + protocolNameToWait
                                        + ")");
                    }
                    catch (InvalidSyntaxException isex)
                    {
                        /*
                         * Not likely so ignore it and assume the event is for
                         * a valid factory.
                         */
                        factoryRefs = null;
                    }
                    if ((factoryRefs != null) && !factoryRefs.isEmpty())
                    {
                        boolean factoryIsRegistered = false;

                        for (ServiceReference<ProtocolProviderFactory> factoryRef
                                : factoryRefs)
                        {
                            if (factory == bundleContext.getService(factoryRef))
                            {
                                factoryIsRegistered = true;
                                break;
                            }
                        }
                        if (!factoryIsRegistered)
                            return;
                    }
                }

                synchronized (storedAccountsAreLoaded)
                {
                    storedAccountsAreLoaded[0] = true;
                    storedAccountsAreLoaded.notify();
                }
            }
        };

        accountManager.addListener(listener);
        try
        {
            bundleToStart.start();

            while (true)
            {
                synchronized (storedAccountsAreLoaded)
                {
                    if (storedAccountsAreLoaded[0])
                    {
                        break;
                    }
                    storedAccountsAreLoaded.wait();
                }
            }
        }
        finally
        {
            accountManager.removeListener(listener);
        }
    }

    /**
     * Prevents the creation of <code>AccountManagerUtils</code> instances.
     */
    private AccountManagerUtils()
    {
    }
}
