
package uk.co.wingpath.modbusgui;

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


public class CommandResult
    implements Command.Result, Xml.Savable
{
    private final ModbusException exception;
    private final long responseTime;

    private static CommandResult NO_ERROR = new CommandResult (null, 0);

    private CommandResult (ModbusException exception, long responseTime)
    {
        this.exception = exception;
        this.responseTime = responseTime;
    }

    public static CommandResult create (ModbusException exception)
    {
        if (exception == null)
            return NO_ERROR;
        return new CommandResult (exception, 0);
    }

    public static CommandResult create (long responseTime)
    {
        if (responseTime == 0)
            return NO_ERROR;
        return new CommandResult (null, responseTime);
    }

    public static CommandResult create ()
    {
        return NO_ERROR;
    }

    @Override
    public ModbusException getException ()
    {
        return exception;
    }

    @Override
    public long getResponseTime ()
    {
        return responseTime;
    }

    @Override
    public boolean matches (Command.Result obj)
    {
        CommandResult r = (CommandResult) obj;
        if (r.exception == null)
            return exception == null;
        return r.exception.matches (exception);
    }

    @Override
    public boolean equals (Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (obj.getClass () != getClass ())
            return false;
        CommandResult r = (CommandResult) obj;
        if (r.exception == null)
            return exception == null;
        return r.exception.equals (exception);
    }

    @Override
    public int hashCode ()
    {
        return exception.hashCode ();
    }

    public void save (Xml.Saver saver)
        throws IOException
    {
        saveException (saver, exception);
    }

    public static void saveException (Xml.Saver saver,
            ModbusException exception)
        throws IOException
    {
        if (exception == null)
            return;
        saver.startTag ("exception");
        saver.saveValue ("error", exception.getErrorCode ());
        saver.saveValue ("is_response", exception.isResponse ());
        saver.endTag ("exception");
    }

    public static Xml.Loader getExceptionLoader (
        Xml.Receiver<ModbusException> receiver)
    {
        return new XmlLoader (receiver);
    }

    private static class XmlLoader
        extends Xml.AbstractLoader
    {
        private final Xml.Receiver<ModbusException> receiver;
        private int error;
        private boolean isResponse;

        XmlLoader (Xml.Receiver<ModbusException> receiver)
        {
            this.receiver = receiver;
            error = 0;
            isResponse = false;
        }

        @Override
        public Xml.Loader startChild (String tag)
        {
            if (tag.equalsIgnoreCase ("error"))
            {
                return new Xml.IntegerLoader (0, 255,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            error = value;
                        }
                    });
            }

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

            return null;
        }

        public void end (String value)
            throws ValueException
        {
            if (!value.equals (""))
                throw new ValueException ("Value not allowed");
            if (error == 0)
                throw new ValueException ("<error> missing");
            String explanation = null;
            if (!isResponse)
            {
                if (error == Modbus.ERROR_TIMED_OUT)
                {
                    explanation = "Timed out";
                }
                else
                {
                    error = Modbus.ERROR_ILLEGAL_DATA_VALUE;
                    explanation = "Invalid response";
                }
            }
            ModbusException exception =
                new ModbusException (error, null, explanation, isResponse);
            receiver.receive (exception);
        }
    }
}

