
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 Command23Panel
    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 CommandRegisterTableModel readModel;
    private final CommandRegisterTableModel writeModel;
    private final CommandValuePanel readPanel;
    private final CommandValuePanel writePanel;

    private final RadixSelector radixSelector;
    private final WComponent<Integer> readAddressField;
    private final WComponent<Integer> readCountField;
    private final WComponent<Integer> writeAddressField;
    private final WComponent<Integer> writeCountField;
    private final TypeSelector typeSelector;
    private final WComponent<Integer> slaveIdField;

    private Command23.Result actualResult;

    public Command23Panel (Frontend frontend, final 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 (Command23.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);

        writeAddressField = new WIntegerField (statusBar, "Write Address",
            0, 65535, 0);
        writeAddressField.setToolTipText (
            "Address of first register to write to");
        writeAddressField.setWidthChars (10);
        writeAddressField.setMirror (mirror);
        leftPanel.addComponent (writeAddressField);

        writeCountField = new WIntegerField (statusBar, "Write Count",
            0, 65535, 1);
        writeCountField.setToolTipText ("Number of values to write");
        writeCountField.setWidthChars (10);
        writeCountField.setMirror (mirror);
        leftPanel.addComponent (writeCountField);

        readAddressField = new WIntegerField (statusBar, "Read Address",
            0, 65535, 0);
        readAddressField.setToolTipText (
            "Address of first register to read from");
        readAddressField.setWidthChars (10);
        readAddressField.setMirror (mirror);
        leftPanel.addComponent (readAddressField);

        readCountField = new WIntegerField (statusBar, "Read Count",
            0, 65535, 1);
        readCountField.setToolTipText ("Number of values to read");
        readCountField.setWidthChars (10);
        readCountField.setMirror (mirror);
        leftPanel.addComponent (readCountField);

        typeSelector = TypeSelector.create (true, true);
        leftPanel.addComponent (typeSelector);

        radixSelector = new RadixSelector (true);
        radixSelector.setRadix (10);
        leftPanel.addComponent (radixSelector);

        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));
        readModel = new CommandRegisterTableModel (
            settings.getBigValue ().getValue (),
            readAddressField.getValue (),
            typeSelector.getValue (),
            radixSelector.getValue (),
            readCountField.getValue (),
            isTester, isEditor, false);
        readPanel = new CommandValuePanel ("Read Data", readModel, statusBar,
            mirror, 5, isEditor);

        writeModel = new CommandRegisterTableModel (
            settings.getBigValue ().getValue (),
            writeAddressField.getValue (),
            typeSelector.getValue (),
            radixSelector.getValue (),
            writeCountField.getValue (),
            false, isEditor, true);
        writePanel = new CommandValuePanel ("Write Data", writeModel,
            statusBar, mirror, 5, isEditor);
        writePanel.setMnemonic (KeyEvent.VK_D);

        rightPanel.add (writePanel);
        rightPanel.add (readPanel);

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

        slaveIdField.addValueListener (listener);
        writePanel.addValueListener (listener);
        readPanel.addValueListener (listener);

        typeSelector.addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                if (!e.isChanging ())
                {
                    Numeric.Type type = typeSelector.getValue ();
                    readModel.setType (type);
                    writeModel.setType (type);
                    setEnabled (true, true);
                }
                listeners.fireValueChanged (e);
            }
        });

        radixSelector.addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                if (!e.isChanging ())
                {
                    int radix = radixSelector.getValue ();
                    readModel.setRadix (radix);
                    writeModel.setRadix (radix);
                }
                listeners.fireValueChanged (e);
            }
        });

        writeAddressField.addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                if (!e.isChanging ())
                    writeModel.setAddress (writeAddressField.getValue ());
                listeners.fireValueChanged (e);
            }
        });

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

        readAddressField.addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
               if (!e.isChanging ())
                    readModel.setAddress (readAddressField.getValue ());
                listeners.fireValueChanged (e);
            }
        });

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

        settings.getBigValue ().addValueListener (new ValueListener ()
        {
            public void valueChanged (ValueEvent e)
            {
                if (!e.isChanging ())
                {
                    BigValueFlags bvf = settings.getBigValue ().getValue ();
                    readModel.setBigValueFlags (bvf);
                    writeModel.setBigValueFlags (bvf);
                }
                listeners.fireValueChanged (e);
            }
        });
    }

    public JPanel getPanel ()
    {
        return panel;
    }

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

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

    public String getName ()
    {
        return "23 Read/Write Registers";
    }

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

    public void initialize ()
    {
    }

    @Override
    public void setEnabled (boolean enabled, boolean enExpected)
    {
        slaveIdField.setEnabled (enabled);
        typeSelector.setEnabled (enabled);
        Numeric.Type type = typeSelector.getValue ();
        if (type.isFloat () || type == Numeric.Type.int1)
        {
            radixSelector.setEnabled (false);
            radixSelector.setRadix (10);
            readModel.setRadix (10);
            writeModel.setRadix (10);
        }
        else
        {
            radixSelector.setEnabled (enabled);
        }
        writePanel.setEnabled (enabled);
        readAddressField.setEnabled (enabled);
        readCountField.setEnabled (enabled);
        writeAddressField.setEnabled (enabled);
        writeCountField.setEnabled (enabled);
        readPanel.setEnabled (enabled);
        if (!enabled || !enExpected)
            readPanel.cancelEditing ();
        readModel.enableExpected (enExpected);
    }

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

    public boolean checkFields ()
    {
        Event.checkIsEventDispatchThread ();
        if (!slaveIdField.checkValue ())
            return false;
        if (!typeSelector.checkValue ())
            return false;
        if (!radixSelector.checkValue ())
            return false;
        if (!writePanel.stopEditing ())
            return false;
        if (!readAddressField.checkValue () ||
            !readCountField.checkValue () ||
            !writeAddressField.checkValue () ||
            !writeCountField.checkValue ())
        {
            return false;
        }
        if (!readPanel.stopEditing ())
            return false;
        statusBar.clear ();
        return true;
    }

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

    public void setCommand (Command command)
    {
        writePanel.cancelEditing ();
        readPanel.cancelEditing ();
        if (command == null)
        {
            int slaveId = settings.getGeneral ().getSlaveId ().getValue ();
            slaveIdField.setValue (slaveId);
            readAddressField.setValue (0);
            readModel.setAddress (0);
            readCountField.setValue (1);
            writeAddressField.setValue (0);
            writeModel.setAddress (0);
            writeCountField.setValue (1);
            final Numeric.Type type = Numeric.Type.int16;
            typeSelector.setValue (type);
            writeModel.setType (type);
            readModel.setType (type);
            final int radix = 10;
            radixSelector.setValue (radix);
            writeModel.setRadix (radix);
            readModel.setRadix (radix);
            writeModel.setSize (writeCountField.getValue ());
            writeModel.reset ();
            readModel.setSize (readCountField.getValue ());
            setActualResult (null);
            setExpectedResult (null);
        }
        else
        {
            Command23 cmd = (Command23) command;
            slaveIdField.setValue (cmd.getSlaveId ());
            readAddressField.setValue (cmd.getReadAddress ());
            readModel.setAddress (cmd.getReadAddress ());
            readCountField.setValue (cmd.getReadCount ());
            writeAddressField.setValue (cmd.getWriteAddress ());
            writeModel.setAddress (cmd.getWriteAddress ());
            writeCountField.setValue (cmd.getWriteData ().length);
            typeSelector.setValue (cmd.getType ());
            writeModel.setType (cmd.getType ());
            readModel.setType (cmd.getType ());
            radixSelector.setValue (cmd.getRadix ());
            writeModel.setRadix (cmd.getRadix ());
            readModel.setRadix (cmd.getRadix ());
            writeModel.setActualData (cmd.getWriteData ().clone ());
            readModel.setSize (readCountField.getValue ());
            setActualResult (cmd.getActualResult ());
            setExpectedResult (cmd.getExpectedResult ());
        }
    }

    public void setActualResult (Command.Result result)
    {
        actualResult = (Command23.Result) result;
        if (result == null)
        {
            readModel.reset ();
        }
        else
        {
            readModel.setActualData (actualResult.getActualReadData ());
            ModbusException exception = actualResult.getException ();
            if (exception != null)
                statusBar.showException (exception);
        }
    }

    private void setExpectedResult (Command.Result result)
    {
        if (isTester)
        {
            if (result == null)
            {
                readModel.resetExpected ();
            }
            else
            {
                Command23.Result r = (Command23.Result) result;
                readModel.setExpectedData (r.getExpectedReadData ());
            }
        }
    }

    @Override
    public void expectActual ()
    {
        if (!isTester)
            return;
        readModel.setExpectedData (readModel.getActualData ());
    }

    public Command23 createCommand (String name, String description,
        ModbusException exception)
    {
        Command23 cmd;
        if (isTester)
        {
            cmd = new Command23 (name,
                description,
                slaveIdField.getValue (),
                readAddressField.getValue (),
                readCountField.getValue (),
                writeAddressField.getValue (),
                writeModel.getActualData (),
                typeSelector.getValue (),
                radixSelector.getValue (),
                exception,
                readModel.getExpectedData ());
        }
        else
        {
            cmd = new Command23 (name,
                description,
                slaveIdField.getValue (),
                readAddressField.getValue (),
                readCountField.getValue (),
                writeAddressField.getValue (),
                writeModel.getActualData (),
                typeSelector.getValue (),
                radixSelector.getValue ());
        }
        cmd.setActualResult (actualResult);
        return cmd;
    }

    public boolean haveValuesChanged (Command command)
    {
        boolean changed = false;
        Command23 cmd = (Command23) command;
        if (slaveIdField.hasValueChanged (cmd.getSlaveId ()))
            changed = true;
        if (readAddressField.hasValueChanged (cmd.getReadAddress ()))
            changed = true;
        if (readCountField.hasValueChanged (cmd.getReadCount ()))
            changed = true;
        if (writeAddressField.hasValueChanged (cmd.getWriteAddress ()))
            changed = true;
        if (writeCountField.hasValueChanged (cmd.getWriteData ().length))
            changed = true;
        if (typeSelector.hasValueChanged (cmd.getType ()))
            changed = true;
        if (radixSelector.hasValueChanged (cmd.getRadix ()))
            changed = true;
        if (writePanel.haveActualValuesChanged (cmd.getWriteData ()))
            changed = true;
        if (isTester)
        {
            Command23.Result result = cmd.getExpectedResult ();
            if (readPanel.haveExpectedValuesChanged (
                result.getExpectedReadData ()))
            {
                changed = true;
            }
        }
        return changed;
    }

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

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

}


