/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2004 Gregor Koukkoullis ( phex <at> kouk <dot> de )
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  --- CVS Information ---
 *  $Id: NetworkPane.java,v 1.26 2004/01/20 17:43:18 gregork Exp $
 */
package phex.gui.dialogs.options;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.*;
import com.jgoodies.forms.layout.FormLayout;

import phex.common.*;
import phex.connection.*;
import phex.gui.common.*;
import phex.utils.*;

public class NetworkPane extends OptionsSettingsPane
{
    private static final String LISTENING_PORT_KEY = "ListeningPort";
    private static final String CONNECTION_TIMEOUT_KEY = "ConnectionTimeout";
    private static final String UP_2_UP_CONNECTIONS_KEY = "UP2UPConnections";
    private static final String UP_2_LEAF_CONNECTIONS_KEY = "UP2LeafConnections";
    private static final String UP_2_PEER_CONNECTIONS_KEY = "UP2PeerConnections";
    private static final String LEAF_2_UP_CONNECTIONS_KEY = "Leaf2UPConnections";
    private static final String LEAF_2_PEER_CONNECTIONS_KEY = "Leaf2PeerConnections";

    private IntegerTextField listeningPortTF;
    private IntegerTextField connectionTimeoutTF;
    private JCheckBox autoConnectChkbx;
    private JCheckBox connectedToLANChkbx;

    private JCheckBox allowToBeUPChkbx;
    private JCheckBox forceToBeUPChkbx;
    private JLabel up2upConnectionsLabel;
    private IntegerTextField up2upConnectionsTF;
    private JLabel up2LeafConnectionsLabel;
    private IntegerTextField up2LeafConnectionsTF;
    private JLabel up2PeerConnectionsLabel;
    private IntegerTextField up2PeerConnectionsTF;

    private JCheckBox allowToBeLeafChkbx;
    private JCheckBox forceUPConnectionsChkbx;
    private JLabel leaf2upConnectionsLabel;
    private IntegerTextField leaf2upConnectionsTF;
    private JLabel leaf2PeerConnectionsLabel;
    private IntegerTextField leaf2PeerConnectionsTF;

    public NetworkPane()
    {
        super( "Network" );
    }

    /**
     * Called when preparing this settings pane for display the first time. Can
     * be overriden to implement the look of the settings pane.
     */
    protected void prepareComponent()
    {
        setLayout( new BorderLayout() );
        
        JPanel contentPanel = new JPanel();
        //JPanel contentPanel = new FormDebugPanel();
        add( contentPanel, BorderLayout.CENTER );
        
        FormLayout layout = new FormLayout(
            "10dlu, right:d, 2dlu, d, " +
            "10dlu, right:d, 2dlu, d, 2dlu:grow", // columns
            "p, 3dlu, p, 3dlu, p, 6dlu, " + 
            "p, 3dlu, p, 3dlu, p, 3dlu, p, 6dlu, " +
            "p, 3dlu, p, 3dlu, p");
        layout.setColumnGroups( new int[][]{{4, 8},{2, 6}} );
        layout.setRowGroups( new int[][]{{3, 5, 9, 11, 13, 17, 19}} );
        
        PanelBuilder builder = new PanelBuilder( contentPanel, layout );
        
        CellConstraints cc = new CellConstraints();
        
        int row = 1;
        builder.addSeparator( Localizer.getString( "NetworkSettings" ),
            cc.xywh( 1, row, 9, 1 ) );

        row += 2;
        builder.addLabel( Localizer.getString( "ListeningPort" ) + ": ",
            cc.xy( 2, row ) );
        listeningPortTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.mListeningPort ), 5, 5 );
        builder.add( listeningPortTF, cc.xy( 4, row ) );
        
        builder.addLabel( Localizer.getString( "ConnectionTimeout" ) + ": ",
            cc.xy( 6, row ) );
        connectionTimeoutTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.mNetConnectionTimeout / 1000 ), 3, 3 );
        builder.add( connectionTimeoutTF, cc.xy( 8, row ) );
        
        row += 2;
        autoConnectChkbx = new JCheckBox( 
            Localizer.getString( "AutoConnectOnStartup" ),
            ServiceManager.sCfg.mAutoConnect );
        builder.add( autoConnectChkbx, cc.xywh( 2, row, 3, 1 ) );
        
        connectedToLANChkbx = new JCheckBox(
            Localizer.getString( "ConnectedToLAN" ),
            ServiceManager.sCfg.connectedToLAN );
        connectedToLANChkbx.setToolTipText( Localizer.getString(
            "TTTConnectedToLAN" ) );
        builder.add( connectedToLANChkbx, cc.xywh( 6, row, 3, 1 ) );
        
        row += 2;
        builder.addSeparator( Localizer.getString( "UltrapeerSettings" ),
            cc.xywh( 1, row, 9, 1 ) );

        row += 2;
        allowToBeUPChkbx = new JCheckBox(
            Localizer.getString( "AllowToBecomeUltrapeer" ),
            ServiceManager.sCfg.allowToBecomeUP );
        allowToBeUPChkbx.setToolTipText( Localizer.getString(
            "TTTAllowToBecomeUltrapeer" ) );
        allowToBeUPChkbx.addActionListener( new CheckboxActionListener() );
        builder.add( allowToBeUPChkbx, cc.xywh( 2, row, 3, 1 ) );
        
        forceToBeUPChkbx = new JCheckBox(
            Localizer.getString( "ForceToBeUltrapeer" ),
            ServiceManager.sCfg.forceToBeUltrapeer );
        forceToBeUPChkbx.setToolTipText( Localizer.getString(
            "TTTForceToBeUltrapeer" ) );
        forceToBeUPChkbx.addActionListener( new CheckboxActionListener() );
        builder.add( forceToBeUPChkbx, cc.xywh( 6, row, 3, 1 ) );
        
        row += 2;
        up2upConnectionsLabel = builder.addLabel(
            Localizer.getString( "ConnectionsToUltrapeers" ) + ": ", cc.xy( 2, row ) );
        up2upConnectionsLabel.setToolTipText( Localizer.getString( "TTTToUltrapeers" ) );
        up2upConnectionsTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.up2upConnections ), 2, 2 );
        up2upConnectionsTF.setToolTipText( Localizer.getString("TTTToUltrapeers" ) );
        builder.add( up2upConnectionsTF, cc.xy( 4, row ) );

        up2LeafConnectionsLabel = builder.addLabel(
            Localizer.getString( "ConnectionsToLeafs" ) + ": ", cc.xy( 6, row ) );
        up2LeafConnectionsLabel.setToolTipText( Localizer.getString("TTTToLeafs" ) );

        up2LeafConnectionsTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.up2leafConnections ), 2, 2 );
        up2LeafConnectionsTF.setToolTipText( Localizer.getString("TTTToLeafs" ) );
        builder.add( up2LeafConnectionsTF, cc.xy( 8, row ) );
        
        row += 2;
        up2PeerConnectionsLabel = builder.addLabel(
            Localizer.getString( "ConnectionsToPeers" ) + ": ", cc.xy( 2, row ) );
        up2PeerConnectionsLabel.setToolTipText( Localizer.getString("TTTToPeers" ) );
        up2PeerConnectionsTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.up2peerConnections ), 2, 2 );
        up2PeerConnectionsTF.setToolTipText( Localizer.getString("TTTToPeers" ) );
        builder.add( up2PeerConnectionsTF, cc.xy( 4, row ) );

        
        row += 2;
        builder.addSeparator( Localizer.getString( "LeafSettings" ),
            cc.xywh( 1, row, 9, 1 ) );
        
        row += 2;
        allowToBeLeafChkbx = new JCheckBox(
            Localizer.getString( "AllowToBecomeLeaf" ),
            ServiceManager.sCfg.allowToBecomeLeaf );
        allowToBeLeafChkbx.setToolTipText( Localizer.getString(
            "TTTAllowToBecomeLeaf" ) );
        allowToBeLeafChkbx.addActionListener( new CheckboxActionListener() );
        builder.add( allowToBeLeafChkbx, cc.xywh( 2, row, 3, 1 ) );

        forceUPConnectionsChkbx = new JCheckBox(
            Localizer.getString( "AcceptOnlyUltrapeers" ),
            ServiceManager.sCfg.forceUPConnections );
        forceUPConnectionsChkbx.setToolTipText( Localizer.getString(
            "TTTAcceptOnlyUltrapeers" ) );
        forceUPConnectionsChkbx.addActionListener( new CheckboxActionListener() );
        builder.add( forceUPConnectionsChkbx, cc.xywh( 6, row, 3, 1 ) );

        row +=2;
        leaf2upConnectionsLabel = builder.addLabel(
            Localizer.getString( "ConnectionsToUltrapeers" ) + ": ", cc.xy( 2, row ) );
        leaf2upConnectionsLabel.setToolTipText( Localizer.getString("TTTToUltrapeers" ) );
        
        leaf2upConnectionsTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.leaf2upConnections ), 2, 2 );
        leaf2upConnectionsTF.setToolTipText( Localizer.getString("TTTToUltrapeers" ) );
        builder.add( leaf2upConnectionsTF, cc.xy( 4, row ) );

        leaf2PeerConnectionsLabel = builder.addLabel(
            Localizer.getString( "ConnectionsToPeers" ) + ": ", cc.xy( 6, row ));
        leaf2PeerConnectionsLabel.setToolTipText( Localizer.getString("TTTToPeers" ) );

        leaf2PeerConnectionsTF = new IntegerTextField(
            String.valueOf( ServiceManager.sCfg.leaf2peerConnections ), 2, 2 );
        leaf2PeerConnectionsTF.setToolTipText( Localizer.getString("TTTToPeers" ) );
        builder.add( leaf2PeerConnectionsTF, cc.xy( 8, row ) );

        refreshEnableState();
    }

    /**
     * Override this method if you like to verify inputs before storing them.
     * A input dictionary is given to the pane. It can be used to store values
     * like error flags or prepared values for saving. The dictionary is given
     * to every settings pane checkInput(), displayErrorMessage() and
     * saveAndApplyChanges() method.
     * When the input has been flaged as invalid with the method setInputValid()
     * the method displayErrorMessage() is called directly after return of
     * checkInput() and the focus is given to settings pane.
     * After checking all settings pane without any error the method
     * saveAndApplyChanges() is called for all settings panes to save the
     * changes.
     */
    public void checkInput( HashMap inputDic )
    {
        try
        {
            String listeningPortStr = listeningPortTF.getText();
            Integer listeningPort = new Integer( listeningPortStr );
            inputDic.put( LISTENING_PORT_KEY, listeningPort );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, listeningPortTF );
            setInputValid( inputDic, false );
            return;
        }

        try
        {
            String connTimeoutStr = connectionTimeoutTF.getText();
            Integer connTimeout = new Integer( connTimeoutStr );
            inputDic.put( CONNECTION_TIMEOUT_KEY, connTimeout );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, connectionTimeoutTF );
            setInputValid( inputDic, false );
            return;
        }

        try
        {
            String up2upConnStr = up2upConnectionsTF.getText();
            Integer up2upConn = new Integer( up2upConnStr );
            inputDic.put( UP_2_UP_CONNECTIONS_KEY, up2upConn );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, up2upConnectionsTF );
            setInputValid( inputDic, false );
            return;
        }

        try
        {
            String up2leafConnStr = up2LeafConnectionsTF.getText();
            Integer up2leafConn = new Integer( up2leafConnStr );
            inputDic.put( UP_2_LEAF_CONNECTIONS_KEY, up2leafConn );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, up2LeafConnectionsTF );
            setInputValid( inputDic, false );
            return;
        }

        try
        {
            String up2peerConnStr = up2PeerConnectionsTF.getText();
            Integer up2peerConn = new Integer( up2peerConnStr );
            inputDic.put( UP_2_PEER_CONNECTIONS_KEY, up2peerConn );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, up2PeerConnectionsTF );
            setInputValid( inputDic, false );
            return;
        }

        try
        {
            String leaf2upConnStr = leaf2upConnectionsTF.getText();
            Integer leaf2upConn = new Integer( leaf2upConnStr );
            inputDic.put( LEAF_2_UP_CONNECTIONS_KEY, leaf2upConn );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, leaf2upConnectionsTF );
            setInputValid( inputDic, false );
            return;
        }

        try
        {
            String leaf2peerConnStr = leaf2PeerConnectionsTF.getText();
            Integer leaf2peerConn = new Integer( leaf2peerConnStr );
            inputDic.put( LEAF_2_PEER_CONNECTIONS_KEY, leaf2peerConn );
        }
        catch ( NumberFormatException exp )
        {
            inputDic.put( NUMBER_FORMAT_ERROR_KEY, leaf2PeerConnectionsTF );
            setInputValid( inputDic, false );
            return;
        }

        setInputValid( inputDic, true );
    }

    /**
     * When isInputValid() returns a false this method is called.
     * The input dictionary should contain the settings pane specific information
     * of the error.
     * The settings pane should override this method to display a error
     * message. Before calling the method the focus is given to the
     * settings pane.
     */
    public void displayErrorMessage( HashMap inputDic )
    {
        if ( inputDic.containsKey( NUMBER_FORMAT_ERROR_KEY ) )
        {
            displayNumberFormatError( inputDic );
        }
    }

    /**
     * Override this method if you like to apply and save changes made on
     * settings pane. To trigger saving of the configuration if any value was
     * changed call triggerConfigSave().
     */
    public void saveAndApplyChanges( HashMap inputDic )
    {
        Integer listeningPortInt = (Integer) inputDic.get(
            LISTENING_PORT_KEY );
        int listeningPort = listeningPortInt.intValue();
        if ( ServiceManager.sCfg.mListeningPort != listeningPort )
        {
            ServiceManager.sCfg.mListeningPort = listeningPort;
            OptionsSettingsPane.triggerConfigSave( inputDic );

            IncomingListener incomingListener =
                NetworkManager.getInstance().getIncomingListener();
            try
            {
                incomingListener.restart();
            }
            catch (Exception e)
            {
                GUIUtils.showErrorMessage(
                    Localizer.getString( "FailedToListenOnNewPort" ),
                    Localizer.getString( "ListenerError" ) );
            }
        }

        Integer connectionTimeoutInt = (Integer) inputDic.get( CONNECTION_TIMEOUT_KEY );
        int connectionTimeout = connectionTimeoutInt.intValue() * 1000;
        if ( ServiceManager.sCfg.mNetConnectionTimeout != connectionTimeout )
        {
            ServiceManager.sCfg.mNetConnectionTimeout = connectionTimeout;
            OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        boolean autoConnect = autoConnectChkbx.isSelected();
        if ( ServiceManager.sCfg.mAutoConnect !=
             autoConnect )
        {
             ServiceManager.sCfg.mAutoConnect = autoConnect;
             OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        boolean connectedToLAN = connectedToLANChkbx.isSelected();
        if ( ServiceManager.sCfg.connectedToLAN !=
             connectedToLAN )
        {
             ServiceManager.sCfg.connectedToLAN = connectedToLAN;
             OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        boolean allowToBecomeLeaf = allowToBeLeafChkbx.isSelected();
        if ( ServiceManager.sCfg.allowToBecomeLeaf !=
             allowToBecomeLeaf )
        {
             ServiceManager.sCfg.allowToBecomeLeaf = allowToBecomeLeaf;
             OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        boolean forceUPConnection = forceUPConnectionsChkbx.isSelected();
        if ( ServiceManager.sCfg.forceUPConnections !=
             forceUPConnection )
        {
             ServiceManager.sCfg.forceUPConnections = forceUPConnection;
             OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        boolean allowToBeUP = allowToBeUPChkbx.isSelected();
        if ( ServiceManager.sCfg.allowToBecomeUP != allowToBeUP )
        {
             ServiceManager.sCfg.allowToBecomeUP = allowToBeUP;
             OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        boolean forceToBeUP = forceToBeUPChkbx.isSelected();
        if ( ServiceManager.sCfg.forceToBeUltrapeer != forceToBeUP )
        {
             ServiceManager.sCfg.forceToBeUltrapeer = forceToBeUP;
             OptionsSettingsPane.triggerConfigSave( inputDic );
        }


        Integer up2upConnectionsInt = (Integer) inputDic.get( UP_2_UP_CONNECTIONS_KEY );
        int up2upConnections = up2upConnectionsInt.intValue();
        if ( ServiceManager.sCfg.up2upConnections != up2upConnections )
        {
            ServiceManager.sCfg.up2upConnections = up2upConnections;
            OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        Integer up2leafConnectionsInt = (Integer) inputDic.get( UP_2_LEAF_CONNECTIONS_KEY );
        int up2leafConnections = up2leafConnectionsInt.intValue();
        if ( ServiceManager.sCfg.up2leafConnections != up2leafConnections )
        {
            ServiceManager.sCfg.up2leafConnections = up2leafConnections;
            OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        Integer up2peerConnectionsInt = (Integer) inputDic.get( UP_2_PEER_CONNECTIONS_KEY );
        int up2peerConnections = up2peerConnectionsInt.intValue();
        if ( ServiceManager.sCfg.up2peerConnections != up2peerConnections )
        {
            ServiceManager.sCfg.up2peerConnections = up2peerConnections;
            OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        Integer leaf2upConnectionsInt = (Integer) inputDic.get( LEAF_2_UP_CONNECTIONS_KEY );
        int leaf2upConnections = leaf2upConnectionsInt.intValue();
        if ( ServiceManager.sCfg.leaf2upConnections != leaf2upConnections )
        {
            ServiceManager.sCfg.leaf2upConnections = leaf2upConnections;
            OptionsSettingsPane.triggerConfigSave( inputDic );
        }

        Integer leaf2peerConnectionsInt = (Integer) inputDic.get( LEAF_2_PEER_CONNECTIONS_KEY );
        int leaf2peerConnections = leaf2peerConnectionsInt.intValue();
        if ( ServiceManager.sCfg.leaf2peerConnections != leaf2peerConnections )
        {
            ServiceManager.sCfg.leaf2peerConnections = leaf2peerConnections;
            OptionsSettingsPane.triggerConfigSave( inputDic );
        }
    }

    private void refreshEnableState()
    {
        up2LeafConnectionsLabel.setEnabled( allowToBeUPChkbx.isSelected() );
        up2LeafConnectionsTF.setEnabled( allowToBeUPChkbx.isSelected() );
        up2PeerConnectionsLabel.setEnabled( allowToBeUPChkbx.isSelected() );
        up2PeerConnectionsTF.setEnabled( allowToBeUPChkbx.isSelected() );
        up2upConnectionsLabel.setEnabled( allowToBeUPChkbx.isSelected() );
        up2upConnectionsTF.setEnabled( allowToBeUPChkbx.isSelected() );
        forceToBeUPChkbx.setEnabled( allowToBeUPChkbx.isSelected() );

        forceUPConnectionsChkbx.setEnabled( allowToBeLeafChkbx.isSelected() );
        leaf2PeerConnectionsLabel.setEnabled( allowToBeLeafChkbx.isSelected() );
        leaf2PeerConnectionsTF.setEnabled( allowToBeLeafChkbx.isSelected() );
        leaf2upConnectionsLabel.setEnabled( allowToBeLeafChkbx.isSelected() );
        leaf2upConnectionsTF.setEnabled( allowToBeLeafChkbx.isSelected() );
    }

    class CheckboxActionListener implements ActionListener
    {
        public void actionPerformed( ActionEvent e )
        {
            Object source = e.getSource();
            if ( source == forceToBeUPChkbx )
            {
                forceUPConnectionsChkbx.setSelected( false );
            }
            else if ( source == forceUPConnectionsChkbx )
            {
                forceToBeUPChkbx.setSelected( false );
            }
            else
            {
                refreshEnableState();
            }
        }
    }
}