
package uk.co.wingpath.modbusgui;

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

public class Command8Panel
    implements CommandPanel
{
    private final Frontend frontend;
    private final Settings settings;
    private final ValueEventSource listeners;
    private final boolean isTester;
    private final boolean isEditor;
    private boolean highlight;

    private final StatusBar statusBar;
    private final GridBagPanel panel;
    private final WComponent<Integer> subFunctionField;
    private final WIntegerField sendDataField;
    private final WNumericField actualRcvdDataField;
    private final WNumericPatternField expectedRcvdDataField;
    private final RadixSelector radixSelector;
    private final WComponent<Integer> slaveIdField;
    private final JLabel actualLabel;
    private final JLabel expectedLabel;

    private Command8.Result actualResult;

    public Command8Panel (Frontend frontend, Settings settings,
        final StatusBar statusBar, MirrorField mirror, final boolean isTester, boolean isEditor)
    {
        Event.checkIsEventDispatchThread ();
        this.frontend = frontend;
        this.settings = settings;
        this.statusBar = statusBar;
        this.isTester = isTester;
        this.isEditor = isEditor;
        listeners = new ValueEventSource ();
        highlight = true;
        actualResult = null;

        panel = new GridBagPanel ();
        panel.addTitle (Command8.typeName, 3);

        int slaveId = settings.getGeneral ().getSlaveId ().getValue ();
        slaveIdField = new WIntegerField (statusBar, "Slave ID",
            0, 255, slaveId);
        slaveIdField.setToolTipText ("Slave identifier");
        slaveIdField.setWidthChars (4);
        slaveIdField.setMnemonic (KeyEvent.VK_I);
        slaveIdField.setMirror (mirror);
        GridBagConstraints constraints = panel.createConstraints ();
        panel.add (slaveIdField.getLabel (), constraints);
        constraints.gridx = 1;
        constraints.gridwidth = 2;
        panel.add (slaveIdField.getComponent (), constraints);

        subFunctionField = new WComboBox<Integer> ("Sub-function",
            new Integer [] {
                Modbus.DIAG_RETURN_QUERY_DATA,
                Modbus.DIAG_RESTART_COMM_OPTION,
                Modbus.DIAG_RETURN_DIAGNOSTIC_REGISTER,
                Modbus.DIAG_CLEAR_COUNTERS,
                Modbus.DIAG_RETURN_BUS_MESSAGE_COUNT,
                Modbus.DIAG_RETURN_BUS_COMM_ERROR_COUNT,
                Modbus.DIAG_RETURN_BUS_EXCEPTION_COUNT,
                Modbus.DIAG_RETURN_SLAVE_MESSAGE_COUNT,
                Modbus.DIAG_RETURN_SLAVE_NO_RESPONSE_COUNT,
                Modbus.DIAG_RETURN_SLAVE_NAK_COUNT,
                Modbus.DIAG_RETURN_SLAVE_BUSY_COUNT,
                Modbus.DIAG_RETURN_BUS_CHARACTER_OVERRUN_COUNT,
                Modbus.DIAG_RETURN_OVERRUN_ERROR_COUNT,
                Modbus.DIAG_CLEAR_OVERRUN_COUNTER,
            },
            new String [] {
                "00 Query Data",
                "01 Restart Comm",
                "02 Diagnostic Register",
                "10 Clear Counters",
                "11 Bus Message Count",
                "12 Bus Comm Error Count",
                "13 Bus Exception Count",
                "14 Slave Message Count",
                "15 Slave No Response Count",
                "16 Slave NAK Count",
                "17 Slave Busy Count",
                "18 Bus Char Overrun Count",
                "19 Overrun Error Count",
                "20 Clear Overrun Counter",
            });
        subFunctionField.setToolTipText ("Diagnostic sub-function code");
        subFunctionField.setToolTipText (
            new String [] {
                "00 Return Query Data",
                "01 Restart Comm Option",
                "02 Return Diagnostic Register",
                "10 Clear Counters",
                "11 Return Bus Message Count",
                "12 Return Bus Comm Error Count",
                "13 Return Bus Exception Count",
                "14 Return Slave Message Count",
                "15 Return Slave No Response Count",
                "16 Return Slave NAK Count",
                "17 Return Slave Busy Count",
                "18 Return Bus Character Overrun Count",
                "19 Return Overrun Error Count",
                "20 Clear Overrun Counter",
            });
        subFunctionField.setMnemonic (KeyEvent.VK_F);
        constraints = panel.createConstraints ();
        panel.add (subFunctionField.getLabel (), constraints);
        constraints.gridx = 1;
        constraints.gridwidth = 2;
        panel.add (subFunctionField.getComponent (), constraints);

        radixSelector = new RadixSelector (false);
        radixSelector.setRadix (10);
        constraints = panel.createConstraints ();
        panel.add (radixSelector.getLabel (), constraints);
        constraints.gridx = 1;
        constraints.gridwidth = 2;
        panel.add (radixSelector.getComponent (), constraints);

        sendDataField = new WIntegerField (statusBar, "Send Data",
            0, 65535, 0);
        sendDataField.setRadix (radixSelector.getValue ());
        sendDataField.setToolTipText ("Data to send in request");
        sendDataField.setMnemonic (KeyEvent.VK_D, 5);
        sendDataField.setWidthChars (17);
        sendDataField.setMirror (mirror);
        constraints = panel.createConstraints ();
        panel.add (sendDataField.getLabel (), constraints);
        constraints.gridx = 1;
        constraints.gridwidth = 2;
        panel.add (sendDataField.getComponent (), constraints);

        actualRcvdDataField = new WNumericField (statusBar, "Received Data");
        actualRcvdDataField.setType (Numeric.Type.uint16);
        actualRcvdDataField.setRadix (radixSelector.getValue ());
        actualRcvdDataField.setToolTipText ("Data received in response");
        actualRcvdDataField.setEditable (false);
        actualRcvdDataField.setWidthChars (17);

        if (isTester)
        {
            actualLabel = new JLabel ("Actual Result");
            expectedLabel = new JLabel ("Expected Result");

            expectedRcvdDataField = new WNumericPatternField (statusBar,
                null, true);
            expectedRcvdDataField.setType (Numeric.Type.uint16);
            expectedRcvdDataField.setRadix (radixSelector.getValue ());
            expectedRcvdDataField.setToolTipText (
                "Expected data received in response");
            expectedRcvdDataField.setWidthChars (17);
            expectedRcvdDataField.setMirror (mirror, "Received Data");

            constraints = panel.createConstraints ();
            constraints.gridx = 1;
            panel.add (expectedLabel, constraints);
            constraints.gridx = 2;
            panel.add (actualLabel, constraints);

            constraints = panel.createConstraints ();
            panel.add (actualRcvdDataField.getLabel (), constraints);
            constraints.gridx = 1;
            panel.add (expectedRcvdDataField.getComponent (), constraints);
            constraints.gridx = 2;
            panel.add (actualRcvdDataField.getComponent (), constraints);
        }
        else
        {
            actualLabel = null;
            expectedLabel = null;
            expectedRcvdDataField = null;

            constraints = panel.createConstraints ();
            panel.add (actualRcvdDataField.getLabel (), constraints);
            constraints.gridx = 1;
            constraints.gridwidth = 2;
            panel.add (actualRcvdDataField.getComponent (), constraints);
            constraints.gridy++;
        }

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

        slaveIdField.addValueListener (listener);
        subFunctionField.addValueListener (listener);
        sendDataField.addValueListener (listener);
        if (isTester)
            expectedRcvdDataField.addValueListener (listener);

        radixSelector.addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                Event.checkIsEventDispatchThread ();
                statusBar.clear ();
                if (!e.isChanging ())
                {
                    int radix = radixSelector.getValue ();
                    sendDataField.setRadix (radix);
                    actualRcvdDataField.setRadix (radix);
                    if (isTester)
                        expectedRcvdDataField.setRadix (radix);
                }
                listeners.fireValueChanged (e);
            }
        });
    }

    public JPanel getPanel ()
    {
        return panel;
    }

    public String getTitle ()
    {
        return Command8.typeName;
    }

    public String getTag ()
    {
        return Command8.tag;
    }

    public String getName ()
    {
        return Command8.typeName;
    }

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

    public void initialize ()
    {
    }

    @Override
    public void setEnabled (boolean enabled, boolean enExpected)
    {
        slaveIdField.setEnabled (enabled);
        subFunctionField.setEnabled (enabled);
        sendDataField.setEnabled (enabled);
        actualRcvdDataField.setEnabled (enabled);
        if (isTester)
        {
            actualLabel.setEnabled (enabled);
            expectedLabel.setEnabled (enabled && enExpected);
            expectedRcvdDataField.setEnabled (enabled && enExpected);
        }
        radixSelector.setEnabled (enabled);
    }

    @Override
    public void highlightErrors (boolean highlight)
    {
        if (highlight != this.highlight)
        {
            this.highlight = highlight;
            highlightErrors ();
        }
    }

    public boolean checkFields ()
    {
        Event.checkIsEventDispatchThread ();
        if (!slaveIdField.checkValue ())
            return false;
        if (!subFunctionField.checkValue ())
            return false;
        if (!sendDataField.checkValue ())
            return false;
        if (isTester && !expectedRcvdDataField.checkValue ())
            return false;
        return true;
    }

    public void setSlaveId (int slaveId)
    {
        slaveIdField.setValue (slaveId);
    }

    private void highlightErrors ()
    {
        if (!isTester)
            return;
        Numeric.Value d = actualRcvdDataField.getValue ();
        Numeric.Pattern ed = expectedRcvdDataField.getValue ();
        actualRcvdDataField.setBackground (!ed.matches (d) ?
            Gui.COLOUR_BACKGROUND_ERROR :
            Gui.COLOUR_BACKGROUND_INACTIVE);
    }

    public void setCommand (Command command)
    {
        if (command == null)
        {
            int slaveId = settings.getGeneral ().getSlaveId ().getValue ();
            slaveIdField.setValue (slaveId);
            subFunctionField.setValue (Modbus.DIAG_RETURN_QUERY_DATA);
            final int radix = 10;
            radixSelector.setValue (radix);
            sendDataField.setRadix (radix);
            sendDataField.setValue (0);
            actualRcvdDataField.setRadix (radix);
            if (isTester)
                expectedRcvdDataField.setRadix (radix);
            setExpectedResult (null);
            setActualResult (null);
        }
        else
        {
            Command8 cmd = (Command8) command;
            slaveIdField.setValue (cmd.getSlaveId ());
            subFunctionField.setValue (cmd.getSubFunction ());
            int radix = cmd.getRadix ();
            radixSelector.setValue (radix);
            sendDataField.setRadix (radix);
            sendDataField.setValue (cmd.getSendData ());
            actualRcvdDataField.setRadix (radix);
            if (isTester)
                expectedRcvdDataField.setRadix (radix);
            setExpectedResult (cmd.getExpectedResult ());
            setActualResult (cmd.getActualResult ());
        }
        highlightErrors ();
    }

    public void setActualResult (Command.Result result)
    {
        actualResult = (Command8.Result) result;
        if (result == null)
        {
            actualRcvdDataField.setValue (Numeric.Type.uint16.undef);
            actualRcvdDataField.setBackground (Gui.COLOUR_BACKGROUND_INACTIVE);
        }
        else
        {
            actualRcvdDataField.setValue (actualResult.getActualData ());
            ModbusException exception = actualResult.getException ();
            if (exception != null)
                statusBar.showException (exception);
        }
        highlightErrors ();
    }

    private void setExpectedResult (Command.Result result)
    {
        if (isTester)
        {
            if (result == null)
            {
                expectedRcvdDataField.setValue (
                    Numeric.Type.uint16.emptyPattern);
            }
            else
            {
                Command8.Result r = (Command8.Result) result;
                expectedRcvdDataField.setValue (r.getExpectedData ());
            }
            highlightErrors ();
        }
    }

    @Override
    public void expectActual ()
    {
        if (!isTester)
            return;
        Numeric.Value value = actualRcvdDataField.getValue ();
        expectedRcvdDataField.setValue (new Numeric.Pattern (value));
        highlightErrors ();
    }

    public Command8 createCommand (String name, String description,
        ModbusException exception)
    {
        Command8 cmd;
        if (isTester)
        {
            cmd = new Command8 (name,
                description,
                slaveIdField.getValue (),
                subFunctionField.getValue (),
                radixSelector.getValue (),
                sendDataField.getValue (),
                exception,
                expectedRcvdDataField.getValue ());
        }
        else
        {
            cmd = new Command8 (name,
                description,
                slaveIdField.getValue (),
                subFunctionField.getValue (),
                radixSelector.getValue (),
                sendDataField.getValue ());
        }
        cmd.setActualResult (actualResult);
        return cmd;
    }

    public boolean haveValuesChanged (Command command)
    {
        boolean changed = false;
        Command8 cmd = (Command8) command;
        if (slaveIdField.hasValueChanged (cmd.getSlaveId ()))
            changed = true;
        if (subFunctionField.hasValueChanged (cmd.getSubFunction ()))
            changed = true;
        if (radixSelector.hasValueChanged (cmd.getRadix ()))
            changed = true;
        if (sendDataField.hasValueChanged (cmd.getSendData ()))
            changed = true;
        if (isTester)
        {
            Command8.Result result = cmd.getExpectedResult ();
            if (expectedRcvdDataField.hasValueChanged (
                result.getExpectedData ()))
            {
                changed = true;
            }
        }
        return changed;
    }

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

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


