
package uk.co.wingpath.modbusgui;

import java.io.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.event.Event;
import uk.co.wingpath.io.*;
import uk.co.wingpath.util.*;
import uk.co.wingpath.gui.*;

public class SerialInterfacePanel
    implements SettingsSubPanel
{
    private static final String [] SPEED_NAMES =
        {
            "1200",
            "2400",
            "4800",
            "9600",
            "19200",
            "38400",
            "57600",
            "115200",
	        "230400",
	        "460800",
            "921600"
        };

    private static final String [] PARITY_NAMES = { "None", "Odd", "Even" };
    private static final String [] PARITY_VALUES = { "none", "odd", "even" };

    private static final String [] DATA_BITS_NAMES = { "7", "8" };
    private static final Integer [] DATA_BITS_VALUES = { 7, 8 };

    private static final String [] STOP_BITS_NAMES = { "1", "2" };
    private static final Integer [] STOP_BITS_VALUES = { 1, 2 };

    private static final String [] RTS_NAMES =
        { "High", "Flow control", "RS485" };
    private static final String [] RTS_VALUES = { "high", "flow", "rs485" };

    private final Frontend frontend;

    private WEditableComboBox portSelector;
    private WEditableComboBox speedSelector;
    private WComponent<String> paritySelector;
    private WComponent<Integer> databitsSelector;
    private WComponent<Integer> stopbitsSelector;
    private WComponent<String> rtsSelector;
    private final StatusBar statusBar;
    private GridBagPanel panel;
    private final ValueEventSource listeners;

    public SerialInterfacePanel (Frontend frontend, final StatusBar statusBar)
    {
        Event.checkIsEventDispatchThread ();
        this.frontend = frontend;
        this.statusBar = statusBar;

        listeners = new ValueEventSource ();

        panel = new GridBagPanel ();

        ValueListener listener = new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    Event.checkIsEventDispatchThread ();
                    listeners.fireValueChanged (e);
                }
            };

        boolean vertical = false;

        portSelector = new WEditableComboBox (statusBar, "Port");
        portSelector.setWidthChars (6);
        portSelector.setToolTipText ("Name of serial port");
        portSelector.setMnemonic (KeyEvent.VK_P);
        portSelector.addValueListener (listener);
        portSelector.addPopupMenuListener (
            new PopupMenuListener ()
            {
                public void popupMenuWillBecomeVisible(PopupMenuEvent e)
                {
                    portSelector.setItems (SerialConnection.isAvailable () ?
                        SerialConnection.getPortNames () : null);
                }

                public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
                {
                }

                public void popupMenuCanceled(PopupMenuEvent e)
                {
                }
            });
        panel.addComponent (portSelector);

        speedSelector = new WEditableComboBox (statusBar, "Speed");
        speedSelector.setItems (SPEED_NAMES);
        speedSelector.setWidthChars (10);
        speedSelector.setAlignment (SwingConstants.RIGHT);
        speedSelector.setToolTipText ("Speed in bits/second");
        speedSelector.setMnemonic (KeyEvent.VK_S);
        speedSelector.addValueListener (listener);
        speedSelector.setVerifier (
            new Verifier ()
            {
                public String verify (String value, boolean isChanging)
                {
                    if (isChanging)
                    {
                        statusBar.clear ();
                        return value;
                    }
                    try
                    {
                        int speed = Integer.parseInt (value);
                        if (speed > 0)
                        {
                            statusBar.clear ();
                            return value;
                        }
                    }
                    catch (NumberFormatException e)
                    {
                    }
                    statusBar.showError ("Speed: Must be a positive integer");
                    return null;
                }
            });
        panel.addComponent (speedSelector);

        paritySelector = new WRadioButtons<String> ("Parity",
            PARITY_VALUES, PARITY_NAMES, PARITY_VALUES [0],
            vertical);
        paritySelector.setToolTipText ("Parity");
        paritySelector.setMnemonic (KeyEvent.VK_Y);
        paritySelector.addValueListener (listener);
        panel.addComponent (paritySelector);

        databitsSelector = new WRadioButtons<Integer> ("Data Bits",
            DATA_BITS_VALUES, DATA_BITS_NAMES,
            DATA_BITS_VALUES [0], vertical);
        databitsSelector.setToolTipText ("Number of data bits");
        databitsSelector.setMnemonic (KeyEvent.VK_D);
        databitsSelector.addValueListener (listener);
        panel.addComponent (databitsSelector);

        stopbitsSelector = new WRadioButtons<Integer> ("Stop Bits",
            STOP_BITS_VALUES, STOP_BITS_NAMES,
            STOP_BITS_VALUES [0], vertical);
        stopbitsSelector.setToolTipText ("Number of stop bits");
        stopbitsSelector.setMnemonic (KeyEvent.VK_O);
        stopbitsSelector.addValueListener (listener);
        panel.addComponent (stopbitsSelector);

        rtsSelector = new WRadioButtons<String> ("RTS Control",
            RTS_VALUES, RTS_NAMES, RTS_VALUES [0], vertical);
        rtsSelector.setToolTipText ("What to do with RTS control line");
        rtsSelector.setToolTipText (new String []
            {
                "Leave high - OK for most purposes",
                "Use for RTS/CTS hardware handshaking",
                "Use for RS485 converter control"
            });
        rtsSelector.setMnemonic (KeyEvent.VK_C);
        rtsSelector.addValueListener (listener);
        panel.addComponent (rtsSelector);

        panel.addFiller ();
        panel.setBorder (BorderFactory.createEmptyBorder (5, 20, 0, 20));
    }

    public JPanel getPanel ()
    {
        return panel;
    }

    public boolean checkValues ()
    {
        if (!portSelector.checkValue () ||
            portSelector.getValue ().equals (""))
        {
            statusBar.showError ("I119", "No serial port specified");
            return false;
        }
        return portSelector.checkValue () &&
            speedSelector.checkValue () &&
            paritySelector.checkValue () &&
            databitsSelector.checkValue () &&
            stopbitsSelector.checkValue () &&
            rtsSelector.checkValue ();
    }

    private int getSpeed ()
    {
        try
        {
            return Integer.parseInt (speedSelector.getValue ());
        }
        catch (NumberFormatException e)
        {
            // Shouldn't happen since user input is verified.
            return 0;
        }
    }

    public void getValuesFromModel (Object model)
    {
        SerialSettings serialSettings = (SerialSettings) model;
        portSelector.setValue (serialSettings.getPortName ());
        speedSelector.setValue ("" + serialSettings.getSpeed ());
        paritySelector.setValue (serialSettings.getParity ());
        databitsSelector.setValue (serialSettings.getDataBits ());
        stopbitsSelector.setValue (serialSettings.getStopBits ());
        rtsSelector.setValue (serialSettings.getRts ());
    }

    public void putValuesToModel (Object model)
    {
        SerialSettings serialSettings = (SerialSettings) model;
        serialSettings.setPortName (portSelector.getValue ());
        serialSettings.setSpeed (getSpeed ());
        serialSettings.setParity (paritySelector.getValue ());
        serialSettings.setDataBits (databitsSelector.getValue ());
        serialSettings.setStopBits (stopbitsSelector.getValue ());
        serialSettings.setRts (rtsSelector.getValue ());
        serialSettings.fireValueChanged ();
    }

    public boolean haveValuesChanged (Object model)
    {
        boolean changed = false;
        SerialSettings serialSettings = (SerialSettings) model;
        if (portSelector.hasValueChanged (serialSettings.getPortName ()))
            changed = true;
        if (speedSelector.hasValueChanged ("" + serialSettings.getSpeed ()))
            changed = true;
        if (paritySelector.hasValueChanged (serialSettings.getParity ()))
            changed = true;
        if (databitsSelector.hasValueChanged (serialSettings.getDataBits ()))
            changed = true;
        if (stopbitsSelector.hasValueChanged (serialSettings.getStopBits ()))
            changed = true;
        if (rtsSelector.hasValueChanged (serialSettings.getRts ()))
            changed = true;
        return changed;
    }

    public String getName ()
    {
        return "Serial";
    }

    public void setEnabled (boolean enabled, BackendState state)
    {
        Event.checkIsEventDispatchThread ();
        portSelector.setEnabled (enabled && state.isStopped);
        speedSelector.setEnabled (enabled && state.isStopped);
        paritySelector.setEnabled (enabled);
        databitsSelector.setEnabled (enabled);
        stopbitsSelector.setEnabled (enabled);
        rtsSelector.setEnabled (enabled);
    }

    public void addValueListener (ValueListener l)
    {
        listeners.addListener (l);
    }

    public void removeValueListener (ValueListener l)
    {
        listeners.removeListener (l);
    }

    public String getTag ()
    {
        return "serial";
    }

    public String getPort ()
    {
        return portSelector.getValue ();
    }
}


