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

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

import javax.swing.*;
import javax.swing.text.*;

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

/**
 * A TransferHandler that we use to handle copying, pasting and DnD operations
 * in our <tt>ChatPanel</tt>. The string handler is heavily inspired
 * by Sun's <tt>DefaultTransferHandler</tt> with the main difference being that
 * we only accept pasting of plain text. We do this in order to avoid HTML
 * support problems that appear when pasting formatted text into our editable
 * area.
 *
 * @author Emil Ivov
 * @author Yana Stamcheva
 */
public class ChatTransferHandler
    extends ExtendedTransferHandler
{
    /**
     * The data flavor used when transferring <tt>UIContact</tt>s.
     */
    protected static final DataFlavor uiContactDataFlavor
        = new DataFlavor(UIContact.class, "UIContact");

    /**
     * This class logger.
     */
    private final static Logger logger
        = Logger.getLogger(ChatTransferHandler.class);

    /**
     * The data flavor used when transferring <tt>File</tt>s under Linux.
     */
    private static DataFlavor uriListFlavor;
    static
    {
         try
         {
             uriListFlavor =
                 new DataFlavor("text/uri-list;class=java.lang.String");
         } catch (ClassNotFoundException e)
         {
            // can't happen
             logger.error("", e);
         }
    }

    /**
     * The chat panel involved in the copy/paste/DnD operation.
     */
    private final ChatPanel chatPanel;

    /**
     * Constructs the <tt>ChatTransferHandler</tt> by specifying the
     * <tt>ChatPanel</tt> we're currently dealing with.
     *
     * @param chatPanel the <tt>ChatPanel</tt> we're currently dealing with
     */
    public ChatTransferHandler(ChatPanel chatPanel)
    {
        this.chatPanel = chatPanel;
    }

    /**
     * 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(uiContactDataFlavor) || f.equals(uriListFlavor))
                return true;
        }

        return super.canImport(comp, flavor);
    }

    /**
     * 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
    @SuppressWarnings("unchecked") //the case is taken care of
    public boolean importData(JComponent comp, Transferable t)
    {
        if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
        {
            try
            {
                Object o = t.getTransferData(DataFlavor.javaFileListFlavor);

                if (o instanceof java.util.Collection)
                {
                    Collection<File> files = (Collection<File>) o;

                    for(File file: files)
                        chatPanel.sendFile(file);

                    // Otherwise fire files dropped event.
                    return true;
                }
            }
            catch (UnsupportedFlavorException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop files.", e);
            }
            catch (IOException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop files.", e);
            }
        }

        if (t.isDataFlavorSupported(uriListFlavor) && OSUtils.IS_LINUX)
        {
            try
            {
                Object o = t.getTransferData(uriListFlavor);
                boolean dataProcessed = false;

                StringTokenizer tokens = new StringTokenizer((String)o);
                while (tokens.hasMoreTokens())
                {
                    String urlString = tokens.nextToken();
                    URL url = new URL(urlString);
                    File file = new File(
                        URLDecoder.decode(url.getFile(), "UTF-8"));
                    chatPanel.sendFile(file);
                    dataProcessed = true;
                }

                return dataProcessed;
            }
            catch (UnsupportedFlavorException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop files.", e);
            }
            catch (IOException e)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop files.", e);
            }
        }

        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();

                // We only support drag&drop for MetaContacts for now.
                if (!(uiContact instanceof MetaUIContact))
                    return false;

                ChatTransport currentChatTransport
                    = chatPanel.getChatSession().getCurrentChatTransport();

                Iterator<Contact> contacts = ((MetaContact) uiContact
                    .getDescriptor()).getContactsForProvider(
                        currentChatTransport.getProtocolProvider());

                String contact = null;
                if (contacts.hasNext())
                    contact = contacts.next().getAddress();

                if (contact != null)
                {
                    List<String> inviteList = new ArrayList<String>();
                    inviteList.add(contact);
                    chatPanel.inviteContacts(   currentChatTransport,
                                                inviteList, null);

                    return true;
                }
                else
                    new ErrorDialog(
                        null,
                        GuiActivator.getResources().getI18NString(
                            "service.gui.ERROR"),
                        GuiActivator.getResources().getI18NString(
                            "service.gui.CONTACT_NOT_SUPPORTING_CHAT_CONF",
                            new String[]{uiContact.getDisplayName()}))
                    .showDialog();
            }
        }

        if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
        {
            InputContext inputContext = comp.getInputContext();
            if (inputContext != null)
            {
                inputContext.endComposition();
            }
            try
            {
                BufferedReader reader = new BufferedReader(
                    DataFlavor.stringFlavor.getReaderForText(t));

                StringBuilder buffToPaste = new StringBuilder();
                String line = reader.readLine();

                while(line != null)
                {
                    buffToPaste.append(line);

                    //read next line
                    line = reader.readLine();
                    if(line != null)
                        buffToPaste.append("\n");
                }

                ((JTextComponent)comp)
                    .replaceSelection(buffToPaste.toString());
                return true;
            }
            catch (UnsupportedFlavorException ufe)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop string.", ufe);
            }
            catch (IOException ioe)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Failed to drop string.", ioe);
            }
        }

        return false;
    }
}
