
package uk.co.wingpath.modbusgui;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.util.*;
import uk.co.wingpath.gui.*;
import uk.co.wingpath.io.*;

public class InterfaceSettingsPanel
    implements TreeCard
{
    private final ValueEventSource statusListeners;
    private final InterfaceSettings interfaceSettings;
    private final String name;

    private final TcpInterfacePanel tcpPanel;
    private final UdpInterfacePanel udpPanel;
    private final SerialInterfacePanel serialPanel;
    private SettingsSubPanel subPanel;
    private Object subModel;
    private final WComponent<String> interfaceSelector;
    private final WComponent<String> packetSelector;
    private final WComponent<Integer> eomTimeoutField;
    private final WComponent<Integer> idleTimeoutField;
    private final WComponent<Boolean> alwaysRespondCheckBox;
    private final JButton applyButton;
    private final JButton resetButton;
    private final JPanel interfacesPanel;
    private final JPanel outerPanel;
    private final JLabel interfaceLabel;
    private final Frontend frontend;
    private final boolean toMaster;
    private final StatusBar statusBar;

    public InterfaceSettingsPanel (Frontend frontend, final boolean toMaster,
        InterfaceSettings interfaceSettings, String name)
    {
        this.frontend = frontend;
        this.toMaster = toMaster;
        this.interfaceSettings = interfaceSettings;
        statusListeners = new ValueEventSource ();
        if (name == null)
            name = "Interface to " + (toMaster ? "Master" : "Slave");
        this.name = name;

        statusBar = new StatusBar ("interface-" + (toMaster ? "M" : "S"),
            frontend.getHelpViewer ());
        statusBar.addStatusListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    fireStatusChanged ();
                }
            });

        outerPanel = new JPanel ();
        outerPanel.setLayout (new BorderLayout ());
        JLabel heading = Gui.createDialogHeading (name);
        outerPanel.add (heading, BorderLayout.NORTH);
        outerPanel.add (statusBar, BorderLayout.SOUTH);

        JPanel mainPanel = new JPanel ();
        outerPanel.add (mainPanel, BorderLayout.CENTER);
        mainPanel.setLayout (new BorderLayout ());
        ButtonPanel buttonPanel = new ButtonPanel ();
        mainPanel.add (buttonPanel, BorderLayout.SOUTH);

        GridBagPanel panel = new GridBagPanel ();
        mainPanel.add (panel, BorderLayout.CENTER);

        tcpPanel = new TcpInterfacePanel (frontend, toMaster, statusBar);
        udpPanel = new UdpInterfacePanel (frontend, toMaster, statusBar);
        serialPanel = new SerialInterfacePanel (frontend, statusBar);

        subPanel = tcpPanel;
        subModel = interfaceSettings.getTcpSettings ();

        interfaceSelector = new WRadioButtons<String> ("Interface Type",
            new String [] { "tcp", "udp", "serial" },
            new String []
                {
                    tcpPanel.getName (),
                    udpPanel.getName (),
                    serialPanel.getName ()
                },
            interfaceSettings.getInterfaceTag (), false);
        interfaceSelector.setToolTipText ("Type of interface to device");
        interfaceSelector.setMnemonic (KeyEvent.VK_I);
        interfaceSelector.addValueListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    selectPanel ();
                    setButtonsEnabled (hasUnappliedChanges ());
                    enableIdleTimeout ();
                }
            });

        ValueListener guiListener = new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    statusBar.clear ();
                    setButtonsEnabled (hasUnappliedChanges ());
                }
            };

        packetSelector = new WRadioButtons<String> ("Packet Type",
            InterfaceSettings.PACKET_TYPE_TAGS,
            new String [] { "TCP", "RTU", "ASCII" },
            interfaceSettings.getPacketType (), false);
        packetSelector.setToolTipText ("Type of packet");
        packetSelector.setMnemonic (KeyEvent.VK_T);
        packetSelector.getLabel ().setDisplayedMnemonicIndex (7);
        packetSelector.addValueListener (guiListener);

        eomTimeoutField = new WIntegerField (statusBar, "EOM Timeout",
            InterfaceSettings.MIN_EOM_TIMEOUT,
            InterfaceSettings.MAX_EOM_TIMEOUT,
            interfaceSettings.getEomTimeout ());
        eomTimeoutField.setToolTipText (
            "Timeout for end-of-message (in milliseconds)");
        eomTimeoutField.setMnemonic (KeyEvent.VK_E);
        eomTimeoutField.setWidthChars (10);
        eomTimeoutField.addValueListener (guiListener);

        idleTimeoutField = new WIntegerField (statusBar, "Idle timeout",
            0, TcpSettings.MAX_IDLE_TIMEOUT, 0);
        idleTimeoutField.setToolTipText (
            "<html>Period in seconds after which a connection will be " +
            "closed if idle.<br>" +
            "A value of 0 will disable the timeout.");
        idleTimeoutField.setMnemonic (KeyEvent.VK_I);
        idleTimeoutField.setWidthChars (10);
        idleTimeoutField.addValueListener (guiListener);

        alwaysRespondCheckBox = new WCheckBox ("Responses",
            toMaster ? "Always Respond" : "Always Responds",
            interfaceSettings.getAlwaysRespond ());
        alwaysRespondCheckBox.setToolTipText (
            toMaster ?
                "Whether to send response to broadcast requests," +
                    " and use exceptions 10/11" :
                "Whether slave sends response to broadcast requests," +
                    " and uses exceptions 10/11");
        alwaysRespondCheckBox.setMnemonic (KeyEvent.VK_A, 10);
        alwaysRespondCheckBox.addValueListener (guiListener);

        interfaceLabel = new JLabel ("");

        interfacesPanel = new JPanel ();
        interfacesPanel.setLayout (new CardLayout ());

        interfacesPanel.add (tcpPanel.getPanel (), "tcp");
        tcpPanel.addValueListener (guiListener);
        interfacesPanel.add (udpPanel.getPanel (), "udp");
        udpPanel.addValueListener (guiListener);
        interfacesPanel.add (serialPanel.getPanel (), "serial");
        serialPanel.addValueListener (guiListener);

        panel.addComponent (interfaceSelector);
        panel.addComponent (packetSelector);

        panel.addComponent (eomTimeoutField);
        panel.addComponent (idleTimeoutField);
        panel.addComponent (alwaysRespondCheckBox);

        panel.addComponent (interfaceLabel, new JLabel ());

        GridBagConstraints constraints = panel.createConstraints ();
        constraints.gridwidth = 2;
        constraints.insets = new Insets (0, 0, 0, 0);
        panel.add (interfacesPanel, constraints);

        applyButton = buttonPanel.addButton ("Apply", null,
            new ActionListener ()
            {
                public void actionPerformed (ActionEvent e)
                {
                    if (checkValues ())
                        putValuesToModel ();
                }
            });

        resetButton = buttonPanel.addButton ("Reset", null,
            new ActionListener ()
            {
                public void actionPerformed (ActionEvent e)
                {
                    reset ();
                }
            });
        resetButton.setMnemonic (KeyEvent.VK_R);

        buttonPanel.addButton (getHelpAction ());

        frontend.addBackendStateListener (new BackendState.Listener ()
            {
                public void stateChanged (BackendState state)
                {
                    boolean en = (toMaster ? state.isSlave : state.isMaster);
                    interfaceSelector.setEnabled (en && state.isStopped);
                    packetSelector.setEnabled (en && state.isStopped);
                    eomTimeoutField.setEnabled (en && state.isStopped);
                    alwaysRespondCheckBox.setEnabled (en && state.isStopped);
                    interfaceLabel.setEnabled (en && state.isStopped);
                    enableIdleTimeout ();

                    tcpPanel.setEnabled (en, state);
                    udpPanel.setEnabled (en, state);
                    serialPanel.setEnabled (en, state);

                    setButtonsEnabled (en && state.isStopped &&
                        hasUnappliedChanges ());
                }
            });

        // If settings get loaded from file, update the display.
        interfaceSettings.addValueListener (new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    getValuesFromModel ();
                }
            });

        getValuesFromModel ();
    }

    private void enableIdleTimeout ()
    {
        idleTimeoutField.setEnabled (
            (toMaster ? frontend.isSlave () : frontend.isMaster ()) &&
            frontend.isStopped () &&
            (!toMaster || interfaceSelector.getValue ().equals ("tcp")));
    }

    public JPanel getPanel ()
    {
        return outerPanel;
    }

    public String getTag ()
    {
        return toMaster ? "master_interface" : "slave_interface";
    }

    public String getName ()
    {
        return name;
    }

    public Action getHelpAction ()
    {
        return frontend.getHelpAction ("interface_settings");
    }

    public JButton getDefaultButton ()
    {
        return applyButton;
    }

    public String getToolTipText ()
    {
        return null;
    }

    @Override
    public void selected ()
    {
    }

    @Override
    public void reset ()
    {
        getValuesFromModel ();
    }

    public boolean isEnabled ()
    {
        return frontend.isStopped () &&
            (toMaster ? frontend.isSlave () : frontend.isMaster ());
    }

    public boolean hasUnappliedChanges ()
    {
        boolean changed = false;
        if (interfaceSelector.hasValueChanged (
            interfaceSettings.getInterfaceTag ()))
        {
            changed = true;
        }
        if (packetSelector.hasValueChanged (
            interfaceSettings.getPacketType ()))
        {
            changed = true;
        }
        if (eomTimeoutField.hasValueChanged (
            interfaceSettings.getEomTimeout ()))
        {
            changed = true;
        }
        if (idleTimeoutField.hasValueChanged (
            interfaceSettings.getIdleTimeout ()))
        {
            changed = true;
        }
        if (alwaysRespondCheckBox.hasValueChanged (
            interfaceSettings.getAlwaysRespond ()))
        {
            changed = true;
        }
        if (subPanel.haveValuesChanged (subModel))
            changed = true;
        return changed;
    }


    public boolean hasError ()
    {
        return statusBar.hasError ();
    }

    private void selectPanel ()
    {
        String tag = interfaceSelector.getValue ();
        statusBar.clear ();
        if (tag.equals ("serial"))
        {
            if (!SerialConnection.isAvailable ())
            {
                statusBar.showWarning ("Serial comms not available");
                tag = interfaceSettings.getInterfaceTag ();
                if (tag.equals ("serial"))
                    tag = "tcp";
                interfaceSelector.setValue (tag);
                return;
            }
        }
        CardLayout layout = (CardLayout) interfacesPanel.getLayout ();
        layout.show (interfacesPanel, tag);
        if (tag.equals ("tcp"))
        {
            subPanel = tcpPanel;
            subModel = interfaceSettings.getTcpSettings ();
            alwaysRespondCheckBox.setValue (true);
            packetSelector.setValue ("tcp");
        }
        else if (tag.equals ("udp"))
        {
            subPanel = udpPanel;
            subModel = interfaceSettings.getUdpSettings ();
            alwaysRespondCheckBox.setValue (true);
            packetSelector.setValue ("tcp");
        }
        else
        {
            subPanel = serialPanel;
            subModel = interfaceSettings.getSerialSettings ();
            alwaysRespondCheckBox.setValue (false);
            packetSelector.setValue ("rtu");
        }
        interfaceLabel.setText (subPanel.getName () + ":");
        subPanel.getValuesFromModel (subModel);
    }

    private boolean checkValues ()
    {
        if (!interfaceSelector.checkValue () ||
            !packetSelector.checkValue () ||
            !eomTimeoutField.checkValue () ||
            !idleTimeoutField.checkValue () ||
            !alwaysRespondCheckBox.checkValue ())
        {
            return false;
        }

        if (!subPanel.checkValues ())
            return false;

        return true;
    }

    private void setButtonsEnabled (boolean enabled)
    {
        Gui.setEnabled (applyButton, enabled);
        Gui.setEnabled (resetButton, enabled);
        fireStatusChanged ();
    }

    private void getValuesFromModel ()
    {
        statusBar.clear ();
        interfaceSelector.setValue (interfaceSettings.getInterfaceTag ());
        selectPanel ();
        packetSelector.setValue (interfaceSettings.getPacketType ());
        eomTimeoutField.setValue (interfaceSettings.getEomTimeout ());
        idleTimeoutField.setValue (interfaceSettings.getIdleTimeout ());
        alwaysRespondCheckBox.setValue (interfaceSettings.getAlwaysRespond ());
        setButtonsEnabled (false);
    }

    private void putValuesToModel ()
    {
        interfaceSettings.setInterface (interfaceSelector.getValue ());
        interfaceSettings.setPacketType (packetSelector.getValue ());
        interfaceSettings.setEomTimeout (eomTimeoutField.getValue ());
        interfaceSettings.setIdleTimeout (idleTimeoutField.getValue ());
        interfaceSettings.setAlwaysRespond (alwaysRespondCheckBox.getValue ());
        subPanel.putValuesToModel (subModel);
        interfaceSettings.fireValueChanged ();
        setButtonsEnabled (false);
    }

    private void fireStatusChanged ()
    {
        statusListeners.fireValueChanged (this);
    }

    public void addStatusListener (ValueListener l)
    {
        statusListeners.addListener (l);
    }

    public void removeStatusListener (ValueListener l)
    {
        statusListeners.removeListener (l);
    }
}


