
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 CommandCustomPanel
    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 sendPanel;
    private final ByteTablePanel actualReceivePanel;
    private final ByteTablePanel expectedReceivePanel;
    private final WComponent<Integer> functionField;
    private final WComponent<Integer> sendCountField;
    private final WComponent<Integer> receiveCountField;
    private final WComponent<Integer> slaveIdField;

    private CommandCustom.Result actualResult;

    public CommandCustomPanel (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 (CommandCustom.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);

        functionField = new WIntegerField (statusBar, "Function", 0, 255, 1);
        functionField.setToolTipText ("Modbus function code, in decimal");
        functionField.setWidthChars (4);
        functionField.setMnemonic (KeyEvent.VK_F);
        functionField.setMirror (mirror);
        leftPanel.addComponent (functionField);

        sendCountField = new WIntegerField (statusBar, "Send count", 0,
            Modbus.MAX_IMP_PDU_SIZE - 1, 0);
        sendCountField.setToolTipText ("Number of data bytes to send");
        sendCountField.setWidthChars (6);
        sendCountField.setMnemonic (KeyEvent.VK_O);
        sendCountField.setMirror (mirror);
        leftPanel.addComponent (sendCountField);

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

        JPanel rightPanel = new JPanel ();
        constraints.gridx = 2;
        constraints.gridwidth = 1;
        constraints.insets = new Insets (2, 5, 2, 5);
        panel.add (rightPanel, constraints);
        rightPanel.setLayout (new BoxLayout (rightPanel, BoxLayout.Y_AXIS));
        sendPanel = new ByteTablePanel ("Send Data", 0, isEditor, true, false,
            statusBar, isTester ? 4 : 5);
        rightPanel.add (sendPanel);

        expectedReceivePanel = new ByteTablePanel ("Expected Received Data",
            0, isEditor, true, true, statusBar, 4);
        if (isTester)
        {
            rightPanel.add (expectedReceivePanel);
            actualReceivePanel = new ByteTablePanel ("Actual Received Data",
                0, isEditor, false, true, statusBar, 4);
        }
        else
        {
            actualReceivePanel = new ByteTablePanel ("Received Data",
                0, isEditor, false, true, statusBar, 5);
        }
        rightPanel.add (actualReceivePanel);

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

        slaveIdField.addValueListener (listener);
        functionField.addValueListener (listener);

        sendPanel.addValueListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    if (!e.isChanging ())
                        sendCountField.setValue (sendPanel.getData ().length);
                    listeners.fireValueChanged (e);
                }
            });

        sendCountField.addValueListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    if (!e.isChanging ())
                        sendPanel.setSize (sendCountField.getValue ());
                    listeners.fireValueChanged (e);
                }
            });

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

        receiveCountField.addValueListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    if (!e.isChanging ())
                    {
                        int size = receiveCountField.getValue ();
                        expectedReceivePanel.setSize (size);
                        actualReceivePanel.setSize (size);
                        Numeric.Value [] data = expectedReceivePanel.getData ();
                        actualReceivePanel.setCompareData (data);
                    }
                    listeners.fireValueChanged (e);
                }
            });
    }

    public JPanel getPanel ()
    {
        return panel;
    }

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

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

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

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

    public void initialize ()
    {
    }

    @Override
    public void setEnabled (boolean enabled, boolean enExpected)
    {
        slaveIdField.setEnabled (enabled);
        sendPanel.setEnabled (enabled);
        functionField.setEnabled (enabled);
        sendCountField.setEnabled (enabled);
        receiveCountField.setEnabled (enabled && enExpected);
        actualReceivePanel.setEnabled (enabled);
        expectedReceivePanel.setEnabled (enabled && enExpected);
    }

    @Override
    public void highlightErrors (boolean highlight)
    {
    }

    public boolean checkFields ()
    {
        assert EventQueue.isDispatchThread ();
        if (!slaveIdField.checkValue ())
            return false;
        if (!sendPanel.stopEditing ())
            return false;
        if (!functionField.checkValue ())
            return false;
        if (!sendCountField.checkValue ())
            return false;
        if (!receiveCountField.checkValue ())
            return false;
        if (!expectedReceivePanel.stopEditing ())
            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);
            functionField.setValue (1);
            sendCountField.setValue (0);
            sendPanel.setSize (0);
            sendPanel.reset ();
            setExpectedResult (null);
            setActualResult (null);
        }
        else
        {
            CommandCustom cmd = (CommandCustom) command;
            slaveIdField.setValue (cmd.getSlaveId ());
            functionField.setValue (cmd.getFunction ());
            sendCountField.setValue (cmd.getSendData ().length);
            sendPanel.setData (cmd.getSendData ().clone ());
            setExpectedResult (cmd.getExpectedResult ());
            setActualResult (cmd.getActualResult ());
        }
    }

    public void setActualResult (Command.Result result)
    {
        actualResult = (CommandCustom.Result) result;
        if (result == null)
        {
            if (isTester)
                actualReceivePanel.setSize (receiveCountField.getValue ());
            actualReceivePanel.reset ();
        }
        else
        {
            Numeric.Value [] data = actualResult.getReceiveData ();
            actualReceivePanel.setData (data);
            ModbusException exception = actualResult.getException ();
            if (exception != null)
                statusBar.showException (exception);
        }
    }

    private void setExpectedResult (Command.Result result)
    {
        if (!isTester)
            return;
        if (result == null)
        {
            expectedReceivePanel.reset ();
            receiveCountField.setValue (0);
            expectedReceivePanel.setSize (0);
            actualReceivePanel.setCompareData (null);
        }
        else
        {
            CommandCustom.Result r = (CommandCustom.Result) result;
            Numeric.Value [] data = r.getReceiveData ();
            expectedReceivePanel.setData (data);
            receiveCountField.setValue (data.length);
            actualReceivePanel.setCompareData (data);
        }
    }

    @Override
    public void expectActual ()
    {
        if (!isTester)
            return;
        Numeric.Value [] data = actualReceivePanel.getData ();
        expectedReceivePanel.setData (data);
        receiveCountField.setValue (data.length);
        actualReceivePanel.setCompareData (data);
    }

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

    public boolean haveValuesChanged (Command command)
    {
        boolean changed = false;
        CommandCustom cmd = (CommandCustom) command;
        if (slaveIdField.hasValueChanged (cmd.getSlaveId ()))
            changed = true;
        if (functionField.hasValueChanged (cmd.getFunction ()))
            changed = true;
        if (sendCountField.hasValueChanged (cmd.getSendData ().length))
            changed = true;
        if (sendPanel.haveValuesChanged (cmd.getSendData ()))
            changed = true;
        if (isTester)
        {
            CommandCustom.Result result = cmd.getExpectedResult ();
            Numeric.Value [] data = result.getReceiveData ();
            if (receiveCountField.hasValueChanged (data.length))
                changed = true;
            if (expectedReceivePanel.haveValuesChanged (data))
                changed = true;
        }
        return changed;
    }

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

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

