
package uk.co.wingpath.modbus;

import java.util.*;
import uk.co.wingpath.util.*;

/**
* Implementation of {@code ModbusRequestHandler} interface that handles a
* request by forwarding it to other slaves.
* Which slave to forward to is determined by the slave ID in the request.
*/
public class ModbusMultiSlave
    implements ModbusRequestHandler
{
    private final List<ModbusSlave> slaves;

    /**
    * Constructs a {@code ModbusMultiSlave}.
    * The slave has an empty list of slaves that it can forward to.
    */
    public ModbusMultiSlave ()
    {
        slaves = new LinkedList<ModbusSlave> ();
    }

    /**
    * Adds a slave to the list of slaves that messages can be forwarded to.
    * @param slave slave to be added to the list.
    */
    public void add (ModbusSlave slave)
    {
        slaves.add (slave);
    }

    /**
    * Adds slaves to the list of slaves that messages can be forwarded to.
    * To replace the list of slaves, call the {@link #clear} method first.
    * @param slaves slaves to be added.
    */
    public void add (List<ModbusSlave> slaves)
    {
        for (ModbusSlave slave : slaves)
            this.slaves.add (slave);
    }

    /**
    * Removes a slave from the list of slaves that messages can be
    * forwarded to.
    * @param slave slave to be removed from the list.
    */
    public void remove (ModbusSlave slave)
    {
        slaves.remove (slave);
    }

    /**
    * Removes all slaves from the list of slaves.
    */
    public void clear ()
    {
        slaves.clear ();
    }

    /**
    * Returns a copy of the list of slaves that messages can be forwarded to.
    * @return list of slaves.
    */
    public List<ModbusSlave> getSlaves ()
    {
        return new LinkedList<ModbusSlave> (slaves);
    }

    /**
    * Returns the size of the list of slaves that messages can be forwarded to.
    * @return number of slaves.
    */
    public int size ()
    {
        return slaves.size ();
    }

    /**
    * Handles a Modbus request message and returns response message.
    * If the request is a broadcast, it is forwarded to all slaves in the
    * list. Otherwise, it is forwarded to the first slave that has a
    * matching slave ID.
    * @param request the request message.
    * @return the response message.
    * @throws ModbusException if no slave has a matching slave ID, or a
    * matching slave throws a {@code ModbusException}
    * ({@code ModbusException}s from slaves are ignored when handling a
    * broadcast request).
    */
    public ModbusResponse handleRequest (ModbusRequest request)
        throws ModbusException
    {
        int id = request.getSlaveId ();

        for (ModbusSlave slave : slaves)
        {
            if (id == 0)
            {
                try
                {
                    slave.handleRequest (request);
                }
                catch (ModbusException e)
                {
                }
            }
            else if (id == slave.getSlaveId ())
            {
                return slave.handleRequest (request);
            }
        }

        if (id == 0)
            return request.broadcastResponse ();

        throw new ModbusException (Modbus.ERROR_PATH_UNAVAILABLE,
            "S101", "No such slave ID");
    }
}

