
package uk.co.wingpath.modbus;

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

/** 
* This exception is thrown when a Modbus error occurs.
* <p>The exception contains a Modbus error code, a help identifier,
* and an explanatory string.
* The {@link #getMessage getMessage} method returns a two-line message: the
* first line is the standard wording for the error code, and the second line
* is the explanatory string.
*/
public class ModbusException
    extends Exception
    implements Helpful
{
    public static final ModbusException NO_ERROR =
        new ModbusException (Modbus.ERROR_NONE, null, null, false);

    private final int error;
    private final String explanation;
    private final boolean isResponse;
    private final String helpId;

    /**
    * Constructs a ModbusException using the specified error code, response
    * flag, help identifier, and explanation.
    * @param error the Modbus error code.
    * @param helpId the help identifier.
    * If {@code helpId} is {@code null}, the help ID is set to "MEnn",
    * where 'nn' is the zero-padded 2-digit
    * error code in decimal, or to "MEXX" if the error code is invalid.
    * @param explanation explanatory string to be appended to the error code
    * message. This parameter may be {@code null} if no explanation is to
    * be appended.
    * @param isResponse {@code true} if this exception is being constructed
    * from a Modbus error response message.
    */
    public ModbusException (int error, String helpId, String explanation,
        boolean isResponse)
    {
        this.error = error;
        this.helpId = helpId == null ? Modbus.getHelpId (error) : helpId;
        this.explanation = explanation;
        this.isResponse = isResponse;
    }

    /**
    * Constructs a ModbusException using the specified error code, help
    * identifier and explanation.
    * @param error the Modbus error code.
    * @param helpId the help identifier. May be {@code null} if specific
    * help is not available.
    * @param explanation explanatory string to be appended to the error code
    * message. This parameter may be {@code null} if no explanation is to
    * be appended.
    */
    public ModbusException (int error, String helpId, String explanation)
    {
        this (error, helpId, explanation, false);
    }

    /**
    * Constructs a ModbusException using the specified error code and a
    * {@code null} explanation.
    * @param error the Modbus error code.
    */
    public ModbusException (int error)
    {
        this (error, null, null, false);
    }

    /**
    * Constructs a {@code ModbusException} from the supplied
    * Modbus error response.
    * <p>The error code is extracted from the error response packet and used
    * to construct the exception.
    * <p>No explanatory message is used: since the response has come from a
    * slave, no further explanation is available to be added to the error code.
    * <p>The help ID is set to "MEnn", where 'nn' is the zero-padded 2-digit
    * error code in decimal, or to "MEXX" if the error code is invalid.
    * @param response the error response.
    */
    public ModbusException (ModbusResponse response)
        throws ModbusException
    {
        this (response.getByte (0), null, null, true);
    }

    /**
    * Gets the explanatory string.
    * @return the explanation.
    */
    public String getExplanation ()
    {
        return explanation;
    }

    /**
    * Constructs the exception message.
    * <p>If an explanatory string was supplied when the exception was
    * constructed, this method returns a two-line message: the
    * first line is the standard wording for the error code (from
    * {@link Modbus#getErrorMessage}), and the second line
    * is the explanatory string.
    * <p>If no explanatory string was supplied, the method returns a one-line
    * message consisting of the standard wording for the error code.
    * @return the message
    */
    @Override
    public String getMessage ()
    {
        String msg = "";
        if (isResponse)
            msg += "Error response: ";
        msg += Modbus.getErrorMessage (error);
        if (explanation != null)
            msg += "\n" + explanation;
        return msg;
    }

    /**
    * Returns the error code.
    * @return the error code.
    */
    public int getErrorCode ()
    {
        return error;
    }

    /**
    * Was this exception created from a Modbus response message?
    * @return {@code true} if this is a response exception, {@code false}
    * otherwise.
    */
    public boolean isResponse ()
    {
        return isResponse;
    }

    /**
    * Gets the help identifier.
    * @return the help identifier.
    */
    public String getHelpId ()
    {
        return helpId;
    }

    public boolean matches (ModbusException e)
    {
        if (this == e)
            return true;
        if (e == null)
            return false;
        if (e.error != error)
            return false;
        if (e.isResponse != isResponse)
            return false;
        return true;
    }

    @Override
    public boolean equals (Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (obj.getClass () != getClass ())
            return false;
        ModbusException e = (ModbusException) obj;
        if (e.error != error)
            return false;
        if (e.isResponse != isResponse)
            return false;
        if (!e.helpId.equals (helpId))
            return false;
        return true;
    }

    @Override
    public int hashCode ()
    {
        return error;
    }
}


