
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 Command7Panel
    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 WNumericField statusField;
    private final WNumericPatternField expectedStatusField;
    private final WComponent<Integer> slaveIdField;
    private final RadixSelector radixSelector;
    private final JLabel actualLabel;
    private final JLabel expectedLabel;

    private Command7.Result actualResult;

    public Command7Panel (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 (Command7.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);

        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);

        statusField = new WNumericField (statusBar, "Status");
        statusField.setType (Numeric.Type.uint16);
        statusField.setRadix (radixSelector.getValue ());
        statusField.setToolTipText ("Returned exception status");
        statusField.setEditable (false);
        statusField.setWidthChars (17);

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

            expectedStatusField = new WNumericPatternField (statusBar,
                null, true);
            expectedStatusField.setType (Numeric.Type.uint16);
            expectedStatusField.setRadix (radixSelector.getValue ());
            expectedStatusField.setToolTipText ("Expected exception status");
            expectedStatusField.setWidthChars (17);
            expectedStatusField.setMirror (mirror, "Status");

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

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

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

        radixSelector.addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                assert EventQueue.isDispatchThread ();
                statusBar.clear ();
                if (!e.isChanging ())
                {
                    int radix = radixSelector.getValue ();
                    statusField.setRadix (radix);
                    if (isTester)
                        expectedStatusField.setRadix (radix);
                }
                listeners.fireValueChanged (e);
            }
        });
        ValueListener listener = new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                Event.checkIsEventDispatchThread ();
                listeners.fireValueChanged (e);
            }
        };

        slaveIdField.addValueListener (listener);
        if (isTester)
            expectedStatusField.addValueListener (listener);
    }

    public JPanel getPanel ()
    {
        return panel;
    }

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

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

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

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

    public void initialize ()
    {
    }

    @Override
    public void setEnabled (boolean enabled, boolean enExpected)
    {
        slaveIdField.setEnabled (enabled);
        radixSelector.setEnabled (enabled);
        statusField.setEnabled (enabled);
        if (isTester)
        {
            actualLabel.setEnabled (enabled);
            expectedLabel.setEnabled (enabled && enExpected);
            expectedStatusField.setEnabled (enabled && enExpected);
        }
    }

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

    public boolean checkFields ()
    {
        assert EventQueue.isDispatchThread ();
        if (!slaveIdField.checkValue ())
            return false;
        if (isTester && !expectedStatusField.checkValue ())
            return false;
        return true;
    }

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

    private void highlightErrors ()
    {
        if (!isTester)
            return;
        Numeric.Value s = statusField.getValue ();
        Numeric.Pattern es = expectedStatusField.getValue ();
        statusField.setBackground (highlight && !es.matches (s) ?
            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);
            final int radix = 10;
            radixSelector.setValue (radix);
            statusField.setRadix (radix);
            if (isTester)
                expectedStatusField.setRadix (radix);
            setActualResult (null);
            setExpectedResult (null);
        }
        else
        {
            Command7 cmd = (Command7) command;
            slaveIdField.setValue (cmd.getSlaveId ());
            int radix = cmd.getRadix ();
            radixSelector.setValue (radix);
            statusField.setRadix (radix);
            if (isTester)
                expectedStatusField.setRadix (radix);
            setActualResult (cmd.getActualResult ());
            setExpectedResult (cmd.getExpectedResult ());
        }
        highlightErrors ();
    }

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

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

    @Override
    public void expectActual ()
    {
        if (!isTester)
            return;
        expectedStatusField.setValue (
            new Numeric.Pattern (statusField.getValue ()));
        highlightErrors ();
    }

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

    public boolean haveValuesChanged (Command command)
    {
        boolean changed = false;
        Command7 cmd = (Command7) command;
        if (slaveIdField.hasValueChanged (cmd.getSlaveId ()))
            changed = true;
        if (radixSelector.hasValueChanged (cmd.getRadix ()))
            changed = true;
        if (isTester)
        {
            Command7.Result result = cmd.getExpectedResult ();
            if (expectedStatusField.hasValueChanged (
                result.getExpectedStatus ()))
            {
                changed = true;
            }
        }
        return changed;
    }

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

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


