package org.limewire.ui.swing.advanced.connection;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.util.Arrays;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;

import org.limewire.collection.glazedlists.GlazedListsFactory;
import org.limewire.core.api.connection.ConnectionItem;
import org.limewire.core.api.connection.GnutellaConnectionManager;
import org.limewire.friend.api.FriendPresence;
import org.limewire.ui.swing.components.Disposable;
import org.limewire.ui.swing.components.FocusJOptionPane;
import org.limewire.ui.swing.search.FriendPresenceActions;
import org.limewire.ui.swing.table.TableDoubleClickHandler;
import org.limewire.ui.swing.table.TablePopupHandler;
import org.limewire.ui.swing.util.I18n;

import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.swing.DefaultEventTableModel;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.inject.Inject;

/**
 * Display panel for the connection details table.
 */
public class ConnectionDetailPanel extends JPanel implements Disposable {

    /** Manager instance for connection data. */
    private GnutellaConnectionManager gnutellaConnectionManager;
    
    /** Action to remove connection. */
    private Action removeConnectionAction = new RemoveConnectionAction(I18n.tr("Remove"));
    
    /** Action to view files on host. */
    private Action viewLibraryAction = new ViewLibraryAction(I18n.tr("Browse Files"));

    private JScrollPane scrollPane = new JScrollPane();
    private ConnectionTable connectionTable = new ConnectionTable();
    private JPopupMenu popupMenu = new JPopupMenu();
    
    private final FriendPresenceActions remoteHostActions;
    
    /**
     * Constructs the ConnectionDetailPanel to display connections details.
     */
    @Inject
    public ConnectionDetailPanel(GnutellaConnectionManager gnutellaConnectionManager, FriendPresenceActions remoteHostActions) {
        this.gnutellaConnectionManager = gnutellaConnectionManager;
        this.remoteHostActions = remoteHostActions;
        initComponents();
    }
    
    /**
     * Initializes the components in the container.
     */
    private void initComponents() {
        setLayout(new BorderLayout());
        setOpaque(false);
        
        scrollPane.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
        scrollPane.setPreferredSize(new Dimension(120, 120));

        // Set table popup handler to display context menu.
        connectionTable.setPopupHandler(new TablePopupHandler() {
            @Override
            public boolean isPopupShowing(int row) {
                // Always return false to prevent color highlighting when popup
                // menu is displayed. See MouseableTable.MenuHighlightPredicate.
                return false;
            }

            @Override
            public void maybeShowPopup(Component component, int x, int y) {
                // Get target row.
                int row = connectionTable.rowAtPoint(new Point(x, y));
                
                // Change selection to include target row.
                if ((row >= 0) && !connectionTable.isRowSelected(row)) {
                    connectionTable.setRowSelectionInterval(row, row);
                }
                
                // Enable View Library action only if host is connected.
                ConnectionItem[] items = connectionTable.getSelectedConnections();
                if (items.length > 0) {
                    viewLibraryAction.setEnabled(items[0].isConnected());
                }
                
                // Show popup menu.
                popupMenu.show(component, x, y);
            }
        });
        
        // Set table double-click handler to view library.
        connectionTable.setDoubleClickHandler(new TableDoubleClickHandler() {
            @Override
            public void handleDoubleClick(int row) {
                if (row >= 0) {
                    connectionTable.setRowSelectionInterval(row, row);
                }
                viewLibrary();
            }
        });

        // Add View Library action.
        JMenuItem menuItem = new JMenuItem(viewLibraryAction);
        popupMenu.add(menuItem);
        popupMenu.addSeparator();

        // Add Remove action.
        menuItem = new JMenuItem(removeConnectionAction);
        popupMenu.add(menuItem);
        
        add(scrollPane, BorderLayout.CENTER);
        scrollPane.setViewportView(connectionTable);
    }
    
    /**
     * Initializes the data models in the container.
     */
    public void initData() {
        if (!(connectionTable.getModel() instanceof DefaultEventTableModel)) {
            // Create connection list for Swing.  We wrap the actual list in a
            // Swing list to ensure that all events are fired on the UI thread.
            TransformedList<ConnectionItem, ConnectionItem> connectionList = 
                GlazedListsFactory.swingThreadProxyEventList(
                        gnutellaConnectionManager.getConnectionList());

            // Create table format.
            ConnectionTableFormat connectionTableFormat = new ConnectionTableFormat();

            // Set up connection table model.
            connectionTable.setEventList(connectionList, connectionTableFormat);
        }
    }
    
    /**
     * Clears the data models in the container.
     */
    @Override
    public void dispose() {
        connectionTable.clearEventList();
    }

    /**
     * Triggers a refresh of the data being displayed. 
     */
    public void refresh() {
        connectionTable.refresh();
    }

    /**
     * Displays the libraries for all selected connections.
     */
    private void viewLibrary() {
        // Browse hosts and display all selected connections.
        ConnectionItem[] items = connectionTable.getSelectedConnections();
        remoteHostActions.viewLibrariesOf(Collections2.transform(Collections2.filter(Arrays.asList(items), new Predicate<ConnectionItem>() {
            @Override
            public boolean apply(ConnectionItem input) {
                return input.isConnected();
            }
        }), new Function<ConnectionItem, FriendPresence>() {
            @Override
			public FriendPresence apply(ConnectionItem from) {
                return from.getFriendPresence();
            }
        }));
        
        for (ConnectionItem item : items) {
            if(!item.isConnected()) {
                FocusJOptionPane.showMessageDialog(this, 
                    I18n.tr("Unable to view files - not yet connected to host"), 
                    I18n.tr("Connections"), JOptionPane.INFORMATION_MESSAGE);
            }
        }
    }
    
    /**
     * Action to remove the selected connections. 
     */
    private class RemoveConnectionAction extends AbstractAction {
        
        public RemoveConnectionAction(String name) {
            super(name);
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            // Remove all selected connections.
            ConnectionItem[] items = connectionTable.getSelectedConnections();
            for (ConnectionItem item : items) {
                gnutellaConnectionManager.removeConnection(item);
            }
            
            connectionTable.clearSelection();
        }
    }
    
    /**
     * Action to display the library for the selected connections.
     */
    private class ViewLibraryAction extends AbstractAction {
        
        public ViewLibraryAction(String name) {
            super(name);
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            viewLibrary();
        }
    }
}
