package org.limewire.ui.swing.properties;

import static org.limewire.ui.swing.util.I18n.tr;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;

import net.miginfocom.swing.MigLayout;

import org.jdesktop.application.Resource;
import org.limewire.bittorrent.Torrent;
import org.limewire.bittorrent.TorrentPeer;
import org.limewire.bittorrent.TorrentStatus;
import org.limewire.core.api.FilePropertyKey;
import org.limewire.core.api.download.DownloadItem;
import org.limewire.core.api.library.PropertiableFile;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.ui.swing.components.decorators.TableDecorator;
import org.limewire.ui.swing.properties.FileInfoDialog.FileInfoType;
import org.limewire.ui.swing.table.DefaultLimeTableCellRenderer;
import org.limewire.ui.swing.table.StripedJXTable;
import org.limewire.ui.swing.util.FontUtils;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.ui.swing.util.I18n;
import org.limewire.util.StringUtils;

public class FileInfoTransfersPanel implements FileInfoPanel {

    @Resource private Icon lockIcon;
    @Resource private Color foreground;
    @Resource private Font smallFont;
    
    private final JPanel component;
    private DownloadItem download = null;
    
    private DownloadStatusListener downloadStatus;
    
    private final StripedJXTable infoTable;
    private final FileInfoTableModel infoModel;
    
    private final Timer refreshTimer;
    private final JLabel leechersLabel;
    private final JLabel seedersLabel;
    
    private Torrent torrent = null;
    
    public FileInfoTransfersPanel(FileInfoType type, PropertiableFile file, TableDecorator tableDecorator) {
        
        Torrent localTorrent = (Torrent)file.getProperty(FilePropertyKey.TORRENT);
        if(localTorrent != null) {
            torrent = localTorrent;
        }
        
        if(file instanceof DownloadItem) {
            download = (DownloadItem) file;
        }
        
        GuiUtils.assignResources(this);

        component = new JPanel(new MigLayout("fillx, gap 0"));
        
        infoModel = new FileInfoTableModel();
        infoTable = new StripedJXTable(infoModel);
        
        tableDecorator.decorate(infoTable);
        
        infoTable.setCellSelectionEnabled(false);
        infoTable.setShowGrid(false, false);
        infoTable.setEditable(false);
        
        component.add(new JScrollPane(infoTable), "gaptop 10, span, grow, wrap");
        
        String userAgent = file.getPropertyString(FilePropertyKey.USERAGENT);
        if(!StringUtils.isEmpty(userAgent) && type == FileInfoType.UPLOADING_FILE) {
            component.add(createBoldLabel(I18n.tr("Version")), "split 2, gaptop 10");
            component.add(createPlainLabel(userAgent), "wrap");
        }
        
        if (download != null) {
            component.add(createBoldLabel(I18n.tr("Total Completed:")), "split 2, gaptop 10");
            JLabel percentLabel = createPlainLabel("");
            component.add(percentLabel, "wrap");
            
            downloadStatus = new DownloadStatusListener(percentLabel);
            download.addPropertyChangeListener(downloadStatus);
        } 

        leechersLabel = createPlainLabel("");
        seedersLabel = createPlainLabel("");
        
        if(torrent != null) {
            component.add(createBoldLabel(I18n.tr("Total Leechers:")), "split 2");
            component.add(leechersLabel, "wrap");
        
            component.add(createBoldLabel(I18n.tr("Total Seeders:")), "split 2");
            component.add(seedersLabel, "wrap");
        }         
        
        initTable();
        refreshTimer = new Timer(1500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                refresh();
            }
        });
        
        // Update by polling to avoid putting too much of a burden on the ui
        //  with an active torrent.
        refreshTimer.start();
        refresh();
    }
    
    @Override
	public JComponent getComponent() {
        return component;
    }

    @Override
    public boolean hasChanged() {
        return false;
    }

    @Override
    public void save() {
        //component never changes state
    }    

    @Override
    public void updatePropertiableFile(PropertiableFile file) {
        //do nothing
    }
    
    @Override
    public void dispose() {
        if(refreshTimer != null)
            refreshTimer.stop();
        
        if(downloadStatus != null) {
            download.removePropertyChangeListener(downloadStatus);
        }
    }
    
    private void initTable() {        
        TableColumn column = infoTable.getColumn(FileInfoTableModel.ENCRYPTED);
        column.setCellRenderer(new LockRenderer());
        column.setMaxWidth(12);
        column.setMinWidth(12);
        column.setWidth(12);
        
        infoTable.setDefaultRenderer(String.class, new DefaultLimeTableCellRenderer());

        infoTable.getColumnExt(FileInfoTableModel.IP).setComparator(IpPort.IP_COMPARATOR);
        TableColumn ipColumn = infoTable.getColumn(FileInfoTableModel.IP);
        ipColumn.setMinWidth(100);
        ipColumn.setCellRenderer(new IPRenderer());
        
        TableColumn uploadColumn = infoTable.getColumn(FileInfoTableModel.UPLOAD_SPEED);
        uploadColumn.setCellRenderer(new SpeedRenderer());
        
        TableColumn downloadColumn = infoTable.getColumn(FileInfoTableModel.DOWNLOAD_SPEED);
        downloadColumn.setCellRenderer(new SpeedRenderer());
    }
    
    private void refresh() {
        infoModel.clear();
           
        // Add leecher/seeder info if BT
        if (torrent != null) {
            infoModel.addAll(torrent.getTorrentPeers());
            TorrentStatus status = torrent.getStatus();
            seedersLabel.setText((status.getNumComplete() < 0) ? "?" : (""+status.getNumComplete()));
            leechersLabel.setText((status.getNumIncomplete() < 0) ? "?" : (""+status.getNumIncomplete()));
        }
    }
    
    private JLabel createBoldLabel(String text) {
        JLabel label = createPlainLabel(text);
        FontUtils.bold(label);        
        return label;
    }    
    
    private JLabel createPlainLabel(String text) {
        JLabel label = new JLabel(text);
        label.setFont(smallFont);
        label.setForeground(foreground);
        return label;
    }
    
    /**
     * Listens for changes to the download status and updates the dialog.
     */
    private class DownloadStatusListener implements PropertyChangeListener {
        private final JLabel label;
        
        public DownloadStatusListener(JLabel label) {
            this.label = label;
        }
        
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    label.setText(tr("{0}%", download.getPercentComplete()));
                } 
            });
        }
    }
    
    private class LockRenderer extends DefaultLimeTableCellRenderer {

        public LockRenderer() {
            setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
        }
        
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            
            if (value == Boolean.TRUE) {
                setIcon(lockIcon);
            } else {
                setIcon(null);
            }
  
            return this;
        }
    }
    
    private static class FileInfoTableModel extends AbstractTableModel {

        public static final int IP = 0;
        public static final int ENCRYPTED = 1;
        public static final int CLIENT_NAME = 2;
        public static final int UPLOAD_SPEED = 3;
        public static final int DOWNLOAD_SPEED = 4;
        
        private List<TorrentPeer> sources = new ArrayList<TorrentPeer>();
        
        private String[] columnNames = new String[]{tr("Address"),
                "", tr("Client"), tr("Upload"), tr("Download")};
        
        public void clear(){
            sources.clear();
        }
        
        public void addAll(Collection<TorrentPeer> info){
            sources.addAll(info);
            fireTableDataChanged();
        }
       
        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        @Override
        public String getColumnName(int column) {
            if(column < columnNames.length){
                return columnNames[column];
            }
            return null;
        }

        @Override
        public int getRowCount() {
            return sources.size();
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            if (rowIndex >= getRowCount()){
                return null;
            }
            return getColumnValue(sources.get(rowIndex), columnIndex);
        }    
        
        private Object getColumnValue(TorrentPeer info, int column) {
            switch (column){
            case IP:
                try {
                    return new IpPortImpl(info.getIPAddress(), 0);
                } catch (UnknownHostException e) {
                    // This only thrown when no IP address can be found for a host or and with global 
                    //IPv6 addresses that have a scope_id.  
                    return null;
                }
            case ENCRYPTED:
                return info.isEncrypted();
            case CLIENT_NAME:
                return info.getClientName();
            case UPLOAD_SPEED:
                return Long.valueOf(Math.round(info.getUploadSpeed()));
            case DOWNLOAD_SPEED:
                return Long.valueOf(Math.round(info.getDownloadSpeed()));
            }
            return null;
        }  
        
    }

    private static class SpeedRenderer extends DefaultLimeTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            if (value != null){
                value = GuiUtils.formatUnitFromBytesPerSec((Long)value);
            }
            return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        }
    }
    
    private static class IPRenderer extends DefaultLimeTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            if (value != null){
                value = ((IpPort)value).getAddress();
            }
            return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        }
    }
}
