package org.limewire.ui.swing.friends.chat;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Map;

import javax.swing.JOptionPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.text.html.FormSubmitEvent;

import org.limewire.core.api.download.DownloadAction;
import org.limewire.core.api.download.DownloadItem;
import org.limewire.core.api.download.DownloadState;
import org.limewire.core.api.download.ResultDownloader;
import org.limewire.core.api.download.DownloadException;
import org.limewire.core.api.friend.FileMetaDataConverter;
import org.limewire.core.api.search.SearchResult;
import org.limewire.io.InvalidDataException;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import org.limewire.ui.swing.components.FocusJOptionPane;
import org.limewire.ui.swing.util.I18n;
import org.limewire.ui.swing.util.NativeLaunchUtils;
import org.limewire.ui.swing.util.DownloadExceptionHandler;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;

/**
 * HyperlinkListener for links to libraries, form submissions (file offers)
 * that come from chat.
 */
class ChatHyperlinkListener implements javax.swing.event.HyperlinkListener {
    private static final Log LOG = LogFactory.getLog(ChatHyperlinkListener.class);

    private Conversation conversation;

    private final ResultDownloader downloader;
    private final FileMetaDataConverter remoteFileItemFactory;
    private final Provider<DownloadExceptionHandler> downloadExceptionHandler;

    @Inject
    public ChatHyperlinkListener(@Assisted Conversation conversation,
            ResultDownloader downloader,
            FileMetaDataConverter remoteFileItemFactory,
            Provider<DownloadExceptionHandler> downloadExceptionHandler) {
        this.conversation = conversation;
        this.downloader = downloader;
        this.remoteFileItemFactory = remoteFileItemFactory;
        this.downloadExceptionHandler = downloadExceptionHandler;
    }

    @Override
    public void hyperlinkUpdate(HyperlinkEvent e) {
        if (e instanceof FormSubmitEvent) {
            FormSubmitEvent event = (FormSubmitEvent) e;

            //Just pushed the download the file button...
            LOG.debugf("File offer download requested. FileId: {0}", event.getData());

            try {
                String dataStr = event.getData();
                String fileIdEncoded = dataStr.substring(dataStr.indexOf("=")+1).trim();
                String fileId = URLDecoder.decode(fileIdEncoded, "UTF-8");
                downloadFileOffer(fileId);
            } catch(UnsupportedEncodingException uee) {
                throw new RuntimeException(uee); // impossible
            }
        } else if (HyperlinkEvent.EventType.ACTIVATED == e.getEventType()) {
            handleLinkClick(e.getDescription(), e.getURL());
        }
    }

    /**
     *
     * Download the file offer given a file ID.
     * <p>
     * package-private for testing
     *
     * @param fileId identifier to look up the file offer message
     */
    void downloadFileOffer(String fileId) {
        Map<String, MessageFileOffer> fileOffers = conversation.getFileOfferMessages();
        MessageFileOffer msgWithfileOffer = fileOffers.get(fileId);
        SearchResult file = null;

        try {
            file = remoteFileItemFactory.create(msgWithfileOffer.getPresence(), msgWithfileOffer.getFileOffer());
            DownloadItem dl = downloader.addDownload(null, Collections.singletonList(file));

            // Track download states by adding listeners to dl item
            addPropertyListener(dl, msgWithfileOffer);

        } catch (DownloadException e) {
            final SearchResult remoteFileItem = file;
            final MessageFileOffer messageFileOffer = msgWithfileOffer;
            downloadExceptionHandler.get().handleDownloadException(new DownloadAction() {
                @Override
                public void download(File saveFile, boolean overwrite)
                        throws DownloadException {
                    DownloadItem dl = downloader.addDownload(null, Collections.singletonList(remoteFileItem), saveFile, overwrite);
                    addPropertyListener(dl, messageFileOffer);
                }

                @Override
                public void downloadCanceled(DownloadException ignored) {
                    //nothing to do                    
                }

            }, e, true);
        } catch (InvalidDataException ide) {
            // this means the FileMetaData we received isn't well-formed.
            LOG.error("Unable to access remote file", ide);
            FocusJOptionPane.showMessageDialog(null,
                    I18n.tr("Unable to access remote file"),
                    I18n.tr("Hyperlink"), JOptionPane.WARNING_MESSAGE);
        }
    }

    private void handleLinkClick(String linkDescription, URL url) {

        if (ChatDocumentBuilder.LIBRARY_LINK.equals(linkDescription)) {
            ChatFriend libraryChatFriend = conversation.getChatFriend();
            LOG.debugf("Opening a view to {0}'s library", libraryChatFriend.getName());
//            libraryNavigator.selectFriendLibrary(libraryChatFriend.getFriend());
            throw new IllegalStateException("action does't exist");

        } else if (ChatDocumentBuilder.MY_LIBRARY_LINK.equals(linkDescription)) {
            LOG.debugf("Opening a view to my library");
//            libraryNavigator.selectLibrary();
            throw new IllegalStateException("action does't exist");
        } else {
            LOG.debugf("Hyperlink clicked: {0}", linkDescription);
            if (linkDescription.startsWith("magnet")) {
                //TODO: Need to do something with magnet links

            } else if (url != null) {
                NativeLaunchUtils.openURL(url.toString());
            }
        }
    }

    private void addPropertyListener(DownloadItem dl, final MessageFileOffer msgWithfileOffer) {
        dl.addPropertyChangeListener(new PropertyChangeListener() {
               @Override
			public void propertyChange(PropertyChangeEvent evt) {
                   if ("state".equals(evt.getPropertyName())) {
                       DownloadState state = (DownloadState) evt.getNewValue();
                       msgWithfileOffer.setDownloadState(state);
                       conversation.displayMessages();
                   }
               }
           });
    }
}
