package org.limewire.ui.swing.statusbar;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.Timer;

import org.jdesktop.application.Resource;
import org.jdesktop.swingx.JXPanel;
import org.limewire.core.api.connection.ConnectionStrength;
import org.limewire.core.api.connection.GnutellaConnectionManager;
import org.limewire.ui.swing.components.HyperlinkButton;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.ui.swing.util.I18n;

import com.google.inject.Inject;

class ConnectionStatusPanel extends JXPanel {
    
    private final String connectingText = I18n.tr("Connecting");
    
    private final GnutellaConnectionManager gnutellaConnectionManager;
    
    private ConnectionStrength currentStrength;
    
    /** 
     * Link to the currently running connecting "animate" timer
     *  that appends dots every 500ms to the connecting message
     */
    private Timer animateTimer = null;
    
    private JButton tryAgainButton = null;
    
    
    private final JLabel connectionStrengthLabel;
    private final JLabel connectionStatusLabel;
    
    @Resource private Icon noInternet;
    @Resource private Icon disconnected;
    @Resource private Icon connecting;
    @Resource private Icon weak;
    @Resource private Icon weakPlus;
    @Resource private Icon medium;
    @Resource private Icon mediumPlus;
    @Resource private Icon full;
    @Resource private Icon turbo;
    @Resource private Font font;

    @Inject
    ConnectionStatusPanel(GnutellaConnectionManager gnutellaConnectionManager) {
        
        this.gnutellaConnectionManager = gnutellaConnectionManager;
        
        GuiUtils.assignResources(this);
        
        this.setLayout(new BorderLayout());
        this.setOpaque(false);
        this.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
        
        connectionStrengthLabel = new JLabel();
        connectionStrengthLabel.setBorder(BorderFactory.createEmptyBorder(1,0,0,4));
        connectionStatusLabel = new JLabel();
        connectionStatusLabel.setBorder(BorderFactory.createEmptyBorder(0,0,0,4));
        connectionStatusLabel.setFont(font);
        connectionStatusLabel.setForeground(this.getForeground());
                
        this.add(this.connectionStrengthLabel,BorderLayout.WEST);
        this.add(this.connectionStatusLabel,BorderLayout.CENTER);
        
        gnutellaConnectionManager.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if(evt.getPropertyName().equals("strength")) {
                    setConnectionStrength((ConnectionStrength)evt.getNewValue());
                }
            }
        });
        
        // Assume that the program is connecting on startup until 
        //  notified otherwise -- avoids blank status or false positive on
        //  disconnected state 
        setConnectionStrength(ConnectionStrength.CONNECTING);
    }
    
    private void setConnectionStrength(ConnectionStrength strength) {
        
        currentStrength = strength;
        
        String statusMessage = "";
        String tooltipText = "";
        boolean shouldHideStatusLater = false;
        boolean showTryAgain = false;
        Icon strengthIcon = null;
        
        switch(strength) {
        case NO_INTERNET:
            statusMessage = I18n.tr("Connection strength is 0/5");
            tooltipText = I18n.tr("You have no internet connection");
            strengthIcon = noInternet;
            break;
        case DISCONNECTED:
            tooltipText = I18n.tr("Disconnected");
            statusMessage = I18n.tr("You couldn't connect to WireShare");
            strengthIcon = disconnected;
            showTryAgain = true;
            break;
        case CONNECTING:
            statusMessage = connectingText;
            tooltipText = I18n.tr("Connecting...");
            animateConnecting();
            strengthIcon = connecting;
            break;
        case WEAK:
            statusMessage = I18n.tr("Connection strength is 1/5");
            tooltipText = I18n.tr("You have a very weak connection");
            strengthIcon = weak;
            break;
         case WEAK_PLUS:
            statusMessage = I18n.tr("Connection strength is 2/5");
            tooltipText = I18n.tr("You have a weak connection");
            strengthIcon = weakPlus;
            break;            
         case MEDIUM:
            statusMessage = I18n.tr("Connection strength is 3/5");
            tooltipText = I18n.tr("You have a medium connection");
            strengthIcon = medium; 
            break;
         case MEDIUM_PLUS:
             statusMessage = I18n.tr("Connection strength is 4/5");
             tooltipText = I18n.tr("You have a fair connection");
             strengthIcon = mediumPlus; 
             break;            
        case FULL:
            shouldHideStatusLater = true;
            statusMessage = I18n.tr("Connection strength is 5/5");
            tooltipText = I18n.tr("You are fully connected");
            strengthIcon = full;
            break;
        case TURBO:
            shouldHideStatusLater = true;
            statusMessage = I18n.tr("Connection strength is 6/5");
            tooltipText = I18n.tr("You have a turbo-charged connection");
            strengthIcon = turbo; 
            break;
        }
               
        if (shouldHideStatusLater) {
            hideStatusLater();
        }
                    
        connectionStatusLabel.setVisible(true);
        connectionStatusLabel.setText(statusMessage);
        connectionStrengthLabel.setIcon(strengthIcon);
        
        if (tryAgainButton == null) {
            if (showTryAgain) {
                tryAgainButton = new HyperlinkButton(I18n.tr("Try Again"));
                tryAgainButton.setFont(font);
                tryAgainButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        gnutellaConnectionManager.restart();
                    }
                });
                this.add(this.tryAgainButton, BorderLayout.EAST);
            }
        } 
        else {
            tryAgainButton.setVisible(showTryAgain);
        }
        
        setToolTipText(tooltipText);
    }

    @Override
    public void setToolTipText(String totalText) {
        super.setToolTipText(totalText);
        connectionStatusLabel.setToolTipText(totalText);
        connectionStrengthLabel.setToolTipText(totalText);
    }
    
    private void hideStatusLater() {
        
        // We need to gaurd against hiding the status label when moving
        //  from Full or Turbo down to a lower strength by ensuring it 
        //  it is still in the same state it was when the hide was 
        //  scheduled. 
        // NOTE: Don't need to gaurd against double hides
        final ConnectionStrength initialStength = currentStrength;
        
        Timer hideSheduler = new Timer(3000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (initialStength == currentStrength) {
                    connectionStatusLabel.setVisible(false);
                }
            }
        });
        
        hideSheduler.setRepeats(false);
        hideSheduler.start();
    }
    
    private void animateConnecting() {
        
        // Gaurd against running multiple timers if the
        //  connection goes out of connecting and back
        //  quickly ( < 500ms in this case)
        if (animateTimer != null) {
            animateTimer.stop();
        }

        animateTimer = new Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                if (currentStrength != ConnectionStrength.CONNECTING) {
                    // Dispose and stop this timer
                    animateTimer.stop();
                    animateTimer = null;
                    return;
                }
                
                int numDots = (connectionStatusLabel.getText().trim().length()
                            -  connectingText.length() + 1)
                            % 4;

                StringBuffer message = new StringBuffer(connectingText);
                // must be a nicer way of doing this. By always appending same # of .'s or ' '
                // we ensure the positioning on the status bar doesn't move while
                // in connecting mode
                for ( int i=0 ; i<4 ; i++ ) {
                    if(i < numDots)
                        message.append('.');
                    else
                        message.append(' ');
                }
                
                connectionStatusLabel.setText(message.toString());
            }
        });
        
        animateTimer.start();
    }
}
