/*
 * 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.gui.main.call.conference;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

import javax.swing.*;

import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;

/**
 * The invite dialog is the one shown when the user clicks on the conference
 * button in the chat toolbar.
 *
 * @author Yana Stamcheva
 * @author Lyubomir Marinov
 */
public class ConferenceInviteDialog
    extends InviteDialog
{
    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 0L;

    /**
     * The account selector box.
     */
    private final JComboBox accountSelectorBox = new JComboBox();

    /**
     * The last selected account.
     */
    private Object lastSelectedAccount;

    /**
     * The telephony conference into which this instance is to invite
     * participants.
     */
    private final CallConference conference;

    /**
     * The current provider contact source.
     */
    private ContactSourceService currentProviderContactSource;

    /**
     * The current string contact source.
     */
    private ContactSourceService currentStringContactSource;

    /**
     * The previously selected protocol provider, with which this dialog has
     * been instantiated.
     */
    private ProtocolProviderService preselectedProtocolProvider;

    /**
     * Indicates whether this conference invite dialog is associated with a
     * Jitsi Videobridge invite.
     */
    private final boolean isJitsiVideobridge;

    /**
     * Initializes a new <tt>ConferenceInviteDialog</tt> instance which is to
     * invite contacts/participants in a specific telephony conference.
     *
     * @param conference the telephony conference in which the new instance is
     * to invite contacts/participants
     */
    public ConferenceInviteDialog(
                            CallConference conference,
                            ProtocolProviderService preselectedProvider,
                            List<ProtocolProviderService> protocolProviders,
                            final boolean isJitsiVideobridge)
    {
        // Set the correct dialog title depending if we're going to create a
        // video bridge conference call
        super((isJitsiVideobridge
                ? GuiActivator.getResources()
                    .getI18NString("service.gui.INVITE_CONTACT_TO_VIDEO_BRIDGE")
                : GuiActivator.getResources()
                    .getI18NString("service.gui.INVITE_CONTACT_TO_CALL")),
                false);

        this.conference = conference;
        this.preselectedProtocolProvider = preselectedProvider;
        this.isJitsiVideobridge = isJitsiVideobridge;

        if (preselectedProtocolProvider == null)
            initAccountSelectorPanel(protocolProviders);

        // init the list, as we check whether features are supported
        // it may take some time if we have too much contacts
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                initContactSources();

                // Initialize the list of contacts to select from.
                if (preselectedProtocolProvider != null)
                    initContactListData(preselectedProtocolProvider);
                else
                    initContactListData(
                        (ProtocolProviderService) accountSelectorBox
                            .getSelectedItem());
            }
        });

        this.addInviteButtonListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                Collection<UIContact> selectedContacts
                    = destContactList.getContacts(null);

                if (selectedContacts != null && selectedContacts.size() > 0)
                {
                    if (preselectedProtocolProvider == null)
                        preselectedProtocolProvider
                            = (ProtocolProviderService) accountSelectorBox
                                .getSelectedItem();

                    if (isJitsiVideobridge)
                        inviteJitsiVideobridgeContacts(  preselectedProtocolProvider,
                                                    selectedContacts);
                    else
                        inviteContacts(selectedContacts);

                    // Store the last used account in order to pre-select it
                    // next time.
                    ConfigurationUtils.setLastCallConferenceProvider(
                        preselectedProtocolProvider);

                    dispose();
                }
                else
                {
                    // TODO: The underlying invite dialog should show a message
                    // to the user that she should select at least two contacts
                    // in order to create a conference.
                }
            }
        });

        this.addCancelButtonListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                dispose();
            }
        });
    }

    /**
     * Constructs the <tt>ConferenceInviteDialog</tt>.
     */
    public ConferenceInviteDialog()
    {
        this(null, null, null, false);
    }

    /**
     * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
     * already created conference. To use when inviting contacts to an existing
     * conference is needed.
     *
     * @param conference the existing <tt>CallConference</tt>
     */
    public ConferenceInviteDialog(CallConference conference)
    {
        this(conference, null, null, false);
    }

    /**
     * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
     * already created conference. To use when inviting contacts to an existing
     * conference is needed.
     *
     * @param conference the existing <tt>CallConference</tt>
     */
    public ConferenceInviteDialog(
                        CallConference conference,
                        ProtocolProviderService preselectedProtocolProvider,
                        boolean isJitsiVideobridge)
    {
        this(conference, preselectedProtocolProvider, null, isJitsiVideobridge);
    }

    /**
     * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
     * preselected protocol provider to be used and if this is an invite for
     * a video bridge conference.
     *
     * @param protocolProviders the protocol providers list
     * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
     * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
     */
    public ConferenceInviteDialog(
                                List<ProtocolProviderService> protocolProviders,
                                boolean isJitsiVideobridge)
    {
        this(null, null, protocolProviders, isJitsiVideobridge);
    }

    /**
     * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
     * preselected protocol provider to be used and if this is an invite for
     * a video bridge conference.
     *
     * @param selectedConfProvider the preselected protocol provider
     * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
     * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
     */
    public ConferenceInviteDialog(
                                ProtocolProviderService selectedConfProvider,
                                boolean isJitsiVideobridge)
    {
        this(null, selectedConfProvider, null, isJitsiVideobridge);
    }

    /**
     * Initializes the account selector panel.
     *
     * @param protocolProviders the list of protocol providers we'd like to
     * show in the account selector box
     */
    private void initAccountSelectorPanel(
                        List<ProtocolProviderService> protocolProviders)
    {
        JLabel accountSelectorLabel = new JLabel(
            GuiActivator.getResources().getI18NString("service.gui.CALL_VIA"));

        TransparentPanel accountSelectorPanel
            = new TransparentPanel(new BorderLayout());

        accountSelectorPanel.setBorder(
            BorderFactory.createEmptyBorder(5, 5, 5, 5));
        accountSelectorPanel.add(accountSelectorLabel, BorderLayout.WEST);
        accountSelectorPanel.add(accountSelectorBox, BorderLayout.CENTER);

        // Initialize the account selector box.
        if (protocolProviders != null && protocolProviders.size() > 0)
            this.initAccountListData(protocolProviders);
        else
            this.initAccountListData();

        this.accountSelectorBox.setRenderer(new DefaultListCellRenderer()
        {
            private static final long serialVersionUID = 0L;

            @Override
            public Component getListCellRendererComponent(JList list,
                Object value, int index, boolean isSelected,
                boolean cellHasFocus)
            {
                ProtocolProviderService protocolProvider
                     = (ProtocolProviderService) value;

                if (protocolProvider != null)
                {
                    this.setText(
                        protocolProvider.getAccountID().getDisplayName());
                    this.setIcon(
                        ImageLoader.getAccountStatusImage(protocolProvider));
                }

                return this;
            }
        });

        this.accountSelectorBox.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                Object accountSelectorBoxSelectedItem
                    = accountSelectorBox.getSelectedItem();

                if (lastSelectedAccount == null
                    || !lastSelectedAccount
                        .equals(accountSelectorBoxSelectedItem))
                {
                    lastSelectedAccount = accountSelectorBoxSelectedItem;

                    initContactListData(
                        (ProtocolProviderService) accountSelectorBox
                            .getSelectedItem());

                    if (isJitsiVideobridge)
                        destContactList.removeAll();
                }
            }
        });

        this.getContentPane().add(accountSelectorPanel, BorderLayout.NORTH);
    }

    /**
     * Initializes the account selector box with the given list of
     * <tt>ProtocolProviderService</tt>-s.
     *
     * @param protocolProviders the list of <tt>ProtocolProviderService</tt>-s
     * we'd like to show in the account selector box
     */
    private void initAccountListData(
                            List<ProtocolProviderService> protocolProviders)
    {
        Iterator<ProtocolProviderService> providersIter
            = protocolProviders.iterator();

        while (providersIter.hasNext())
        {
            ProtocolProviderService protocolProvider
                = providersIter.next();

            accountSelectorBox.addItem(protocolProvider);
        }

        if (accountSelectorBox.getItemCount() > 0)
            accountSelectorBox.setSelectedIndex(0);
    }

    /**
     * Initializes the account list.
     */
    private void initAccountListData()
    {
        Iterator<ProtocolProviderService> protocolProviders
            = GuiActivator.getUIService().getMainFrame().getProtocolProviders();

        while(protocolProviders.hasNext())
        {
            ProtocolProviderService protocolProvider
                = protocolProviders.next();
            OperationSet opSet
                = protocolProvider.getOperationSet(
                        OperationSetTelephonyConferencing.class);

            if ((opSet != null) && protocolProvider.isRegistered())
                accountSelectorBox.addItem(protocolProvider);
        }

        // Try to select the last used account if available.
        ProtocolProviderService pps
            = ConfigurationUtils.getLastCallConferenceProvider();

        if (pps == null && conference != null)
        {
            /*
             * Pick up the first account from the ones participating in the
             * associated telephony conference which supports
             * OperationSetTelephonyConferencing.
             */
            for (Call call : conference.getCalls())
            {
                ProtocolProviderService callPps = call.getProtocolProvider();

                if (callPps.getOperationSet(
                            OperationSetTelephonyConferencing.class)
                        != null)
                {
                    pps = callPps;
                    break;
                }
            }
        }

        if (pps != null)
            accountSelectorBox.setSelectedItem(pps);
        else if (accountSelectorBox.getItemCount() > 0)
            accountSelectorBox.setSelectedIndex(0);
    }

    /**
     * Initializes contact list sources.
     */
    private void initContactSources()
    {
        DemuxContactSourceService demuxCSService
             = GuiActivator.getDemuxContactSourceService();

        // If the DemuxContactSourceService isn't registered we use the default
        // contact source set.
        if (demuxCSService == null)
            return;

        Iterator<UIContactSource> sourcesIter
            = new ArrayList<UIContactSource>(
                srcContactList.getContactSources()).iterator();

        srcContactList.removeAllContactSources();

        while (sourcesIter.hasNext())
        {
            ContactSourceService contactSource
                = sourcesIter.next().getContactSourceService();

            srcContactList.addContactSource(
                demuxCSService.createDemuxContactSource(contactSource));
        }
    }

    /**
     * Initializes the left contact list with the contacts that could be added
     * to the current chat session.
     * @param protocolProvider the protocol provider from which to initialize
     * the contact list data
     */
    private void initContactListData(ProtocolProviderService protocolProvider)
    {
        this.setCurrentProvider(protocolProvider);

        Iterator<UIContactSource> sourcesIter
            = new ArrayList<UIContactSource>(
                srcContactList.getContactSources()).iterator();

        while (sourcesIter.hasNext())
        {
            ContactSourceService contactSource
                = sourcesIter.next().getContactSourceService();

            if (contactSource instanceof ProtocolAwareContactSourceService)
            {
                ((ProtocolAwareContactSourceService) contactSource)
                    .setPreferredProtocolProvider(
                        OperationSetBasicTelephony.class, protocolProvider);
            }
        }

        srcContactList.removeContactSource(currentProviderContactSource);
        srcContactList.removeContactSource(currentStringContactSource);

        currentProviderContactSource
            = new ProtocolContactSourceServiceImpl(
                                            protocolProvider,
                                            OperationSetBasicTelephony.class);
        currentStringContactSource
            = new StringContactSourceServiceImpl(
                                            protocolProvider,
                                            OperationSetBasicTelephony.class);

        srcContactList.addContactSource(currentProviderContactSource);
        srcContactList.addContactSource(currentStringContactSource);

        srcContactList.applyDefaultFilter();
    }

    /**
     * Invites the contacts to the chat conference.
     *
     * @param contacts the list of contacts to invite
     */
    private void inviteContacts(Collection<UIContact> contacts)
    {
        ProtocolProviderService selectedProvider;
        Map<ProtocolProviderService, List<String>> selectedProviderCallees
            = new HashMap<ProtocolProviderService, List<String>>();
        List<String> callees;

        Iterator<UIContact> contactsIter = contacts.iterator();

        while (contactsIter.hasNext())
        {
            UIContact uiContact = contactsIter.next();

            Iterator<UIContactDetail> contactDetailsIter = uiContact
                .getContactDetailsForOperationSet(
                    OperationSetBasicTelephony.class).iterator();

            // We invite the first protocol contact that corresponds to the
            // invite provider.
            if (contactDetailsIter.hasNext())
            {
                UIContactDetail inviteDetail = contactDetailsIter.next();
                selectedProvider = inviteDetail
                    .getPreferredProtocolProvider(
                        OperationSetBasicTelephony.class);

                if (selectedProvider == null)
                {
                    selectedProvider
                        = (ProtocolProviderService)
                            accountSelectorBox.getSelectedItem();
                }

                if(selectedProvider != null
                    && selectedProviderCallees.get(selectedProvider) != null)
                {
                    callees = selectedProviderCallees.get(selectedProvider);
                }
                else
                {
                    callees = new ArrayList<String>();
                }

                callees.add(inviteDetail.getAddress());
                selectedProviderCallees.put(selectedProvider, callees);
            }
        }

        if(conference != null)
        {
            CallManager.inviteToConferenceCall(
                    selectedProviderCallees,
                    conference);
        }
        else
        {
            CallManager.createConferenceCall(selectedProviderCallees);
        }
    }

    /**
     * Invites the contacts to the chat conference.
     *
     * @param contacts the list of contacts to invite
     */
    private void inviteJitsiVideobridgeContacts(
                                ProtocolProviderService preselectedProvider,
                                Collection<UIContact> contacts)
    {
        List<String> callees = new ArrayList<String>();

        Iterator<UIContact> contactsIter = contacts.iterator();

        while (contactsIter.hasNext())
        {
            UIContact uiContact = contactsIter.next();

            Iterator<UIContactDetail> contactDetailsIter = uiContact
                .getContactDetailsForOperationSet(
                    OperationSetBasicTelephony.class).iterator();

            // We invite the first protocol contact that corresponds to the
            // invite provider.
            if (contactDetailsIter.hasNext())
            {
                UIContactDetail inviteDetail = contactDetailsIter.next();

                callees.add(inviteDetail.getAddress());
            }
        }

        if(conference != null)
        {
            CallManager.inviteToJitsiVideobridgeConfCall(
                    callees.toArray(new String[callees.size()]),
                    conference.getCalls().get(0));
        }
        else
        {
            CallManager.createJitsiVideobridgeConfCall(
                preselectedProvider,
                callees.toArray(new String[callees.size()]));
        }
    }
}
