
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 Command17Panel
    implements CommandPanel
{
    private final Frontend frontend;
    private final Settings settings;
    private final ValueEventSource listeners;
    private final boolean isTester;
    private final boolean isEditor;

    private final StatusBar statusBar;
    private final GridBagPanel panel;
    private final ByteTablePanel actualDataPanel;
    private final ByteTablePanel expectedDataPanel;
    private final WTextField actualDataField;
    private final WTextField expectedDataField;
    private final WComponent<Integer> expectedSizeField;
    private final WComponent<Integer> slaveIdField;

    private Command17.Result actualResult;

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

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

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

        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);
        leftPanel.addComponent (slaveIdField);

        expectedSizeField = new WIntegerField (statusBar, "Expected size",
            0, Modbus.MAX_IMP_PDU_SIZE - 1, 0);
        expectedSizeField.setToolTipText ("Expected number of data bytes");
        expectedSizeField.setWidthChars (6);
        expectedSizeField.setMnemonic (KeyEvent.VK_O);
        expectedSizeField.setMirror (mirror);
        if (isTester)
            leftPanel.addComponent (expectedSizeField);

        GridBagPanel rightPanel = new GridBagPanel ();
        constraints.gridx = 2;
        constraints.gridwidth = 1;
        constraints.insets = new Insets (2, 5, 2, 5);
        panel.add (rightPanel, constraints);

        if (isTester)
        {
            expectedDataPanel =
                new ByteTablePanel ("Expected Data", 0, isEditor, true, true,
                statusBar, 4);
            constraints = rightPanel.createConstraints ();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
            rightPanel.add (expectedDataPanel, constraints);

            expectedDataField = new WTextField (statusBar, null);
            expectedDataField.setWidthChars (30);
            expectedDataField.setMirror (mirror, "Expected Data");
            constraints = rightPanel.createConstraints ();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
            rightPanel.add (expectedDataField.getComponent (), constraints);

            actualDataPanel = new ByteTablePanel ("Actual Data", 0, isEditor,
                false, false, statusBar, 4);
            constraints = rightPanel.createConstraints ();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
            rightPanel.add (actualDataPanel, constraints);

            actualDataField = new WTextField (statusBar, null);
            actualDataField.setWidthChars (30);
            actualDataField.setEditable (false);
            constraints = rightPanel.createConstraints ();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
            rightPanel.add (actualDataField.getComponent (), constraints);

            actualDataPanel.setCompareData (expectedDataPanel.getData ());

            expectedDataPanel.addValueListener (
                new ValueListener ()
                {
                    public void valueChanged (ValueEvent e)
                    {
                        if (!e.isChanging ())
                        {
                            Numeric.Value [] data = expectedDataPanel.getData ();
                            expectedSizeField.setValue (data.length);
                            actualDataPanel.setCompareData (data);
                        }
                        listeners.fireValueChanged (e);
                    }
                });

            expectedDataField.addValueListener (
                new ValueListener ()
                {
                    public void valueChanged (ValueEvent e)
                    {
                        if (e.isChanging ())
                            return;
                        try
                        {
                            String str = expectedDataField.getValue ();
                            Numeric.Value [] data =
                                Numeric.fromByteArray (str.getBytes ("UTF-8"));
                            expectedDataPanel.setData (data);
                            expectedSizeField.setValue (data.length);
                            actualDataPanel.setCompareData (data);
                        }
                        catch (UnsupportedEncodingException ex)
                        {
                            // Shouldn't happen - UTF-8 should be supported.
                            throw new AssertionError ("Unreachable");
                        }
                        listeners.fireValueChanged (e);
                    }
                });

            expectedSizeField.addValueListener (
                new ValueListener ()
                {
                    public void valueChanged (ValueEvent e)
                    {
                        if (!e.isChanging ())
                        {
                            expectedDataPanel.setSize (
                                expectedSizeField.getValue ());
                            Numeric.Value [] data = expectedDataPanel.getData ();
                            actualDataPanel.setCompareData (data);
                        }
                        listeners.fireValueChanged (e);
                    }
                });
        }
        else
        {
            actualDataPanel = new ByteTablePanel ("Received Data", 0, isEditor,
                false, false, statusBar, 5);
            constraints = rightPanel.createConstraints ();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
            rightPanel.add (actualDataPanel, constraints);

            actualDataField = new WTextField (statusBar, null);
            actualDataField.setWidthChars (30);
            actualDataField.setEditable (false);
            constraints = rightPanel.createConstraints ();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.weightx = 1.0;
            rightPanel.add (actualDataField.getComponent (), constraints);

            expectedDataPanel = null;
            expectedDataField = null;
        }

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

        slaveIdField.addValueListener (listener);
    }

    public JPanel getPanel ()
    {
        return panel;
    }

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

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

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

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

    public void initialize ()
    {
    }

    @Override
    public void setEnabled (boolean enabled, boolean enExpected)
    {
        slaveIdField.setEnabled (enabled);
        actualDataPanel.setEnabled (enabled);
        if (isTester)
        {
            expectedSizeField.setEnabled (enabled && enExpected);
            expectedDataPanel.setEnabled (enabled && enExpected);
            expectedDataField.setEnabled (enabled && enExpected);
        }
    }

    @Override
    public void highlightErrors (boolean highlight)
    {
        actualDataPanel.highlightErrors (highlight);
    }

    public boolean checkFields ()
    {
        Event.checkIsEventDispatchThread ();
        statusBar.clear ();
        if (!slaveIdField.checkValue ())
            return false;
        if (isTester)
        {
            if (!expectedDataPanel.stopEditing ())
                return false;
            if (!expectedSizeField.checkValue ())
                return false;
        }
        return true;
    }

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

    public void setCommand (Command command)
    {
        if (command == null)
        {
            int slaveId = settings.getGeneral ().getSlaveId ().getValue ();
            slaveIdField.setValue (slaveId);
            setActualResult (null);
            setExpectedResult (null);
        }
        else
        {
            Command17 cmd = (Command17) command;
            slaveIdField.setValue (cmd.getSlaveId ());
            setActualResult (cmd.getActualResult ());
            setExpectedResult (cmd.getExpectedResult ());
        }
    }

    private void setText (WTextField field, Numeric.Value [] data)
    {
        try
        {
            field.setValue (new String (Numeric.toByteArray (data), "UTF-8"));
        }
        catch (UnsupportedEncodingException e)
        {
            // Shouldn't happen - UTF-8 should be supported.
            throw new AssertionError ("Unreachable");
        }
    }

    public void setActualResult (Command.Result result)
    {
        actualResult = (Command17.Result) result;
        if (result == null)
        {
            actualDataPanel.reset ();
            actualDataField.setValue ("");
        }
        else
        {
            Numeric.Value [] data = actualResult.getData ();
            actualDataPanel.setData (data);
            setText (actualDataField, data);
            ModbusException exception = actualResult.getException ();
            if (exception != null)
                statusBar.showException (exception);
        }
    }

    private void setExpectedResult (Command.Result result)
    {
        if (isTester)
        {
            Numeric.Value [] data;
            if (result == null)
            {
                data = new Numeric.Value [0];
            }
            else
            {
                Command17.Result r = (Command17.Result) result;
                data = r.getData ();
            }
            expectedDataPanel.setData (data);
            setText (expectedDataField, data);
            expectedSizeField.setValue (data.length);
            actualDataPanel.setCompareData (data);
        }
    }

    @Override
    public void expectActual ()
    {
        if (!isTester)
            return;
        Numeric.Value [] data = actualDataPanel.getData ();
        expectedDataPanel.setData (data);
        setText (expectedDataField, data);
        expectedSizeField.setValue (data.length);
        actualDataPanel.setCompareData (data);
    }

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

    public boolean haveValuesChanged (Command command)
    {
        boolean changed = false;
        Command17 cmd = (Command17) command;
        if (slaveIdField.hasValueChanged (cmd.getSlaveId ()))
            changed = true;
        if (isTester)
        {
            Command17.Result result = cmd.getExpectedResult ();
            if (expectedDataPanel.haveValuesChanged (result.getData ()))
                changed = true;
        }
        return changed;
    }

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

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


