/*
 * 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;

import java.awt.datatransfer.*;
import java.awt.im.*;
import java.io.*;
import java.util.*;

import javax.swing.*;

import org.jitsi.service.resources.*;

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

/**
 * A <tt>TransferHandler</tt> that handles dropping of <tt>UIContact</tt>s or
 * <tt>String</tt> addresses on a <tt>CallConference</tt>. Dropping such data on
 * the <tt>CallDialog</tt> will turn a one-to-one <tt>Call</tt> into a telephony
 * conference.
 *
 * @author Yana Stamcheva
 */
public class CallTransferHandler
    extends ExtendedTransferHandler
{
    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 0L;

    /**
     * The data flavor used when transferring <tt>UIContact</tt>s.
     */
    protected static final DataFlavor uiContactDataFlavor
        = new DataFlavor(UIContact.class, "UIContact");

    /**
     * The logger.
     */
    private static final Logger logger
        = Logger.getLogger(CallTransferHandler.class);

    /**
     * The <tt>CallConference</tt> into which the dropped callees are to be
     * invited.
     */
    private final CallConference callConference;

    /**
     * Initializes a new <tt>CallTransferHandler</tt> instance which is to
     * invite dropped callees to a telephony conference specified by a specific
     * <tt>Call</tt> which participates in it.
     *
     * @param call the <tt>Call</tt> which specifies the telephony conference to
     * which dropped callees are to be invited
     */
    public CallTransferHandler(Call call)
    {
        this(call.getConference());
    }

    /**
     * Initializes a new <tt>CallTransferHandler</tt> instance which is to
     * invite dropped callees to a specific <tt>CallConference</tt>.
     *
     * @param callConference the <tt>CallConference</tt> to which dropped
     * callees are to be invited
     */
    public CallTransferHandler(CallConference callConference)
    {
        this.callConference = callConference;
    }

    /**
     * Indicates whether a component will accept an import of the given
     * set of data flavors prior to actually attempting to import it. We return
     * <tt>true</tt> to indicate that the transfer with at least one of the
     * given flavors would work and <tt>false</tt> to reject the transfer.
     * <p>
     * @param comp component
     * @param flavor the data formats available
     * @return  true if the data can be inserted into the component, false
     * otherwise
     * @throws NullPointerException if <code>support</code> is {@code null}
     */
    @Override
    public boolean canImport(JComponent comp, DataFlavor[] flavor)
    {
        for (DataFlavor f : flavor)
        {
            if (f.equals(DataFlavor.stringFlavor)
                    || f.equals(uiContactDataFlavor))
            {
                return (comp instanceof JPanel);
            }
        }
        return false;
    }

    /**
     * Handles transfers to the chat panel from the clip board or a
     * DND drop operation. The <tt>Transferable</tt> parameter contains the
     * data that needs to be imported.
     * <p>
     * @param comp  the component to receive the transfer;
     * @param t the data to import
     * @return  true if the data was inserted into the component and false
     * otherwise
     */
    @Override
    public boolean importData(JComponent comp, Transferable t)
    {
        String callee = null;
        ProtocolProviderService provider = null;

        if (t.isDataFlavorSupported(uiContactDataFlavor))
        {
            Object o = null;

            try
            {
                o = t.getTransferData(uiContactDataFlavor);
            }
            catch (UnsupportedFlavorException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop meta contact.", e);
            }
            catch (IOException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop meta contact.", e);
            }

            if (o instanceof ContactNode)
            {
                UIContact uiContact = ((ContactNode) o).getContactDescriptor();
                Iterator<UIContactDetail> contactDetails
                    = uiContact
                        .getContactDetailsForOperationSet(
                                OperationSetBasicTelephony.class)
                            .iterator();

                while (contactDetails.hasNext())
                {
                    UIContactDetail detail = contactDetails.next();
                    ProtocolProviderService detailProvider
                        = detail.getPreferredProtocolProvider(
                                OperationSetBasicTelephony.class);

                    if (detailProvider != null)
                    {
                        /*
                         * Currently for videobridge conferences we only support
                         * adding contacts via the account with the videobridge
                         */
                        if (callConference.isJitsiVideobridge())
                        {
                            for (Call call : callConference.getCalls())
                            {
                                if (detailProvider == call.getProtocolProvider())
                                {
                                    callee = detail.getAddress();
                                    provider = detailProvider;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            callee = detail.getAddress();
                            provider = detailProvider;
                            break;
                        }
                    }
                }

                if (callee == null)
                {
                    /*
                     * It turns out that the error message to be reported would
                     * like to display information about the account which could
                     * not add the dropped callee to the telephony conference.
                     * Unfortunately, a telephony conference may have multiple
                     * accounts involved. Anyway, choose the first account
                     * involved in the telephony conference.
                     */
                    ProtocolProviderService callProvider
                        = callConference.getCalls().get(0)
                                .getProtocolProvider();

                    ResourceManagementService resources
                        = GuiActivator.getResources();
                    AccountID accountID = callProvider.getAccountID();

                    new ErrorDialog(null,
                            resources.getI18NString("service.gui.ERROR"),
                            resources.getI18NString(
                                    "service.gui.CALL_NOT_SUPPORTING_PARTICIPANT",
                                    new String[]
                                            {
                                                accountID.getService(),
                                                accountID.getUserID(),
                                                uiContact.getDisplayName()
                                            }))
                        .showDialog();
                }
            }
        }
        else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
        {
            InputContext inputContext = comp.getInputContext();

            if (inputContext != null)
                inputContext.endComposition();

            try
            {
                BufferedReader reader
                    = new BufferedReader(
                            DataFlavor.stringFlavor.getReaderForText(t));

                try
                {
                    String line;
                    StringBuilder calleeBuilder = new StringBuilder();

                    while ((line = reader.readLine()) != null)
                        calleeBuilder.append(line);

                    callee = calleeBuilder.toString();
                    /*
                     * The value of the local variable provider will be null
                     * because we have a String only and hence we have no
                     * associated ProtocolProviderService.
                     * CallManager.inviteToConferenceCall will accept it.
                     */
                }
                finally
                {
                    reader.close();
                }
            }
            catch (UnsupportedFlavorException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop string.", e);
            }
            catch (IOException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop string.", e);
            }
        }

        if (callee == null)
            return false;
        else
        {
            Map<ProtocolProviderService, List<String>> callees
                = new HashMap<ProtocolProviderService, List<String>>();

            callees.put(provider, Arrays.asList(callee));
            CallManager.inviteToConferenceCall(callees, callConference);

            return true;
        }
    }
}
