
package uk.co.wingpath.modbusgui;

import java.io.*;
import java.util.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.util.*;
import uk.co.wingpath.xml.*;
import uk.co.wingpath.modbus.*;

public class Command5
    implements Command<CommandResult>
{
    private static class Model
        extends ModbusModel
    {
        private boolean value;

        Model (boolean value, BigValueFlags bigValueFlags)
        {
            super (bigValueFlags, null);
            this.value = value;
            AddressMap map = getAddressMap ();
            map.getCoilMap ().setSize (65536);
            // map.getCoilMap ().setMultiplier (1);
        }

        @Override
        public int getValueSize (int address)
        {
            return 0;
        }

        @Override
        public boolean isSigned (int address)
            throws ModbusException
        {
            return false;
        }

        @Override
        public int getRadix (int address)
            throws ModbusException
        {
            return 10;
        }

        @Override
        public long getLongValue (int address)
        {
            return value ? 0xff : 0;
        }

        @Override
        public int getCoilValueSize ()
        {
            return 0;
        }
    }

    public static final String tag = "cmd_5";
    public static final String typeName = "5 Write Single Coil";

    private final String name;
    private final String description;

    private final int slaveId;
    private final int address;
    private final boolean value;

    private CommandResult actualResult;
    private final CommandResult expectedResult;

    Command5 (String name, String description, int slaveId, int address,
        boolean value, ModbusException exception)
    {
        this.name = name;
        this.description = description;
        this.slaveId = slaveId;
        this.address = address;
        this.value = value;
        expectedResult = CommandResult.create (exception);
        actualResult = null;
    }

    Command5 (String name, String description, int slaveId, int address,
        boolean value)
    {
        this.name = name;
        this.description = description;
        this.slaveId = slaveId;
        this.address = address;
        this.value = value;
        expectedResult = null;
        actualResult = null;
    }

    public int getSlaveId ()
    {
        return slaveId;
    }

    public int getAddress ()
    {
        return address;
    }

    public boolean getValue ()
    {
        return value;
    }

    public CommandResult getActualResult ()
    {
        return actualResult;
    }

    public void setActualResult (CommandResult result)
    {
        actualResult = result;
    }

    public CommandResult getExpectedResult ()
    {
        return expectedResult;
    }

    @Override
    public boolean equals (Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (obj.getClass () != getClass ())
            return false;
        Command5 cmd = (Command5) obj;
        if (!cmd.name.equals (name))
            return false;
        if (!cmd.description.equals (description))
            return false;
        if (cmd.slaveId != slaveId)
            return false;
        if (cmd.address != address)
            return false;
        if (cmd.value != value)
            return false;
        if (expectedResult == null)
            return cmd.expectedResult == null;
        return expectedResult.equals (cmd.expectedResult);
    }

    @Override
    public int hashCode ()
    {
        int hash = 3;
        hash = 29 * hash + name.hashCode ();
        hash = 29 * hash + description.hashCode ();
        hash = 29 * hash + slaveId;
        hash = 29 * hash + address;
        hash = 29 * hash + (value ? 1 : 0);
        return hash;
    }

    @Override
    public String toString ()
    {
        return name;
    }

    public String getDescription ()
    {
        return description;
    }

    public String getTypeName ()
    {
        return typeName;
    }

    public String getTag ()
    {
        return tag;
    }

    public void send (ModbusClient client, Settings settings)
        throws InterruptedException, IOException, ValueException
    {
        try
        {
            BigValueFlags bigValueFlags = settings.getBigValue ().getValue ();
            GeneralSettings gs = settings.getGeneral ();
            int maxPdu = gs.getMaxPdu ().getValue ();
            boolean checkCountLimits =
                gs.getCheckCountLimits ().getValue ();
            boolean strictChecking = gs.getStrictChecking ().getValue ();
            boolean allowLongMessages = gs.getAllowLongMessages ().getValue ();
            ModbusMaster master = new ModbusMaster (
                new Model (value, bigValueFlags),
                client, slaveId, maxPdu, checkCountLimits,
                strictChecking, allowLongMessages, false);
            master.writeCoil (address);
            actualResult = CommandResult.create (master.getResponseTime ());
        }
        catch (ModbusException e)
        {
            actualResult = CommandResult.create (e);
        }
    }

    public void save (Xml.Saver saver)
        throws IOException
    {
        saver.saveValue ("name", name);
        saver.saveValue ("description", description);
        saver.saveValue ("slaveid", slaveId);
        saver.saveValue ("address", address);
        saver.saveValue ("value", value);
        if (expectedResult != null)
            saver.saveValue ("result", expectedResult);
    }

    public static Xml.Loader getXmlLoader (Xml.Receiver<Command> receiver,
        boolean isTester)
    {
        return new XmlLoader (receiver, isTester);
    }

    private static class XmlLoader
        extends Xml.AbstractLoader
    {
        private final Xml.Receiver<Command> receiver;
        private final boolean isTester;
        private String name;
        private String description;
        private int slaveId;
        private int address;
        private boolean value;
        private ModbusException exception;

        XmlLoader (Xml.Receiver<Command> receiver, boolean isTester)
        {
            this.receiver = receiver;
            this.isTester = isTester;
            name = "";
            description = "";
            slaveId = 1;
            address = 0;
            value = false;
            exception = null;
        }

        @Override
        public Xml.Loader startChild (String tag)
        {
            if (tag.equalsIgnoreCase ("name"))
            {
                return new Xml.StringLoader (new Xml.Receiver<String> ()
                {
                    public void receive (String value)
                        throws ValueException
                    {
                        if (value.equals (""))
                            throw new ValueException ("Name missing");
                        name = value;
                    }
                });
            }

            if (tag.equalsIgnoreCase ("description"))
            {
                return new Xml.StringLoader (new Xml.Receiver<String> ()
                {
                    public void receive (String value)
                        throws ValueException
                    {
                        description = value;
                    }
                });
            }

            if (tag.equalsIgnoreCase ("slaveid"))
            {
                return new Xml.IntegerLoader (0, 255,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            slaveId = value;
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("address"))
            {
                return new Xml.IntegerLoader (0, 65535,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            address = value;
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("value"))
            {
                return new Xml.BooleanLoader (
                    new Xml.Receiver<Boolean> ()
                    {
                        public void receive (Boolean val)
                        {
                            value = val;
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("result"))
            {
                return new Xml.AbstractLoader ()
                {
                    @Override
                    public Xml.Loader startChild (String tag)
                    {
                        if (tag.equalsIgnoreCase ("exception"))
                        {
                            return CommandResult.getExceptionLoader (
                                new Xml.Receiver<ModbusException> ()
                                {
                                    public void receive (ModbusException e)
                                    {
                                        exception = e;
                                    }
                                });
                        }

                        return null;
                    }
                };
            }

            return null;
        }

        public void end (String val)
            throws ValueException
        {
            if (!val.equals (""))
                throw new ValueException ("Value not allowed");
            if (name.equals (""))
                throw new ValueException ("<name> missing");
            if (isTester)
            {
                Command5 command = new Command5 (name, description, slaveId,
                    address, value, exception);
                receiver.receive (command);
            }
            else
            {
                Command5 command = new Command5 (name, description, slaveId,
                    address, value);
                receiver.receive (command);
            }
        }
    }
}

