
package uk.co.wingpath.modbusgui;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.util.*;
import uk.co.wingpath.gui.*;
import uk.co.wingpath.xml.*;
import uk.co.wingpath.io.*;

public class InterfaceSettings
    implements Xml.Savable
{
    public static final int MIN_EOM_TIMEOUT = 1;
    public static final int MAX_EOM_TIMEOUT = 10000;
    public static final int MAX_IDLE_TIMEOUT = Integer.MAX_VALUE;
    public static final String [] PACKET_TYPE_TAGS =
        new String [] { "tcp", "rtu", "ascii" };

    private final SerialSettings serialSettings;
    private final TcpSettings tcpSettings;
    private final UdpSettings udpSettings;
    private final boolean toMaster;
    private String interfaceTag;
    private String packetTag;
    private int eomTimeout;
    private boolean alwaysRespond;
    private int idleTimeout;
    private final ValueEventSource listeners;

    public InterfaceSettings (Product product, final boolean toMaster)
    {
        this.toMaster = toMaster;

        tcpSettings = new TcpSettings (toMaster);
        udpSettings = new UdpSettings (toMaster);
        serialSettings = new SerialSettings ();

        interfaceTag = "tcp";
        packetTag = "tcp";
        eomTimeout = 100;
        alwaysRespond = true;
        idleTimeout = 0;
        listeners = new ValueEventSource ();
    }

    public TcpSettings getTcpSettings ()
    {
        return tcpSettings;
    }

    public UdpSettings getUdpSettings ()
    {
        return udpSettings;
    }

    public SerialSettings getSerialSettings ()
    {
        return serialSettings;
    }

    public String getInterfaceTag ()
    {
        return interfaceTag;
    }

    public void setInterface (String tag)
    {
        if (!tag.equals (interfaceTag))
        {
            interfaceTag = tag;
            // Caller should call fireValueChanged after setting all values
            // listeners.fireValueChanged (this);
        }
    }

    public String getPacketType ()
    {
        return packetTag;
    }

    public void setPacketType (String packetTag)
    {
        if (!packetTag.equals (this.packetTag))
        {
            this.packetTag = packetTag;
            // Caller should call fireValueChanged after setting all values
            // listeners.fireValueChanged (this);
        }
    }

    public int getEomTimeout ()
    {
        return eomTimeout;
    }

    public void setEomTimeout (int eomTimeout)
    {
        if (eomTimeout != this.eomTimeout)
        {
            this.eomTimeout = eomTimeout;
            // Caller should call fireValueChanged after setting all values
            // listeners.fireValueChanged (this);
        }
    }

    public boolean getAlwaysRespond ()
    {
        return alwaysRespond;
    }

    public void setAlwaysRespond (boolean alwaysRespond)
    {
        if (alwaysRespond != this.alwaysRespond)
        {
            this.alwaysRespond = alwaysRespond;
            // Caller should call fireValueChanged after setting all values
            // listeners.fireValueChanged (this);
        }
    }

    public int getIdleTimeout ()
    {
        return idleTimeout;
    }

    public void setIdleTimeout (int idleTimeout)
    {
        this.idleTimeout = idleTimeout;
    }

    public void save (Xml.Saver saver)
        throws IOException
    {
        saver.saveValue ("interface",
            interfaceTag.equals ("tcp") ? "socket" :
            interfaceTag.equals ("udp") ? "udp" : interfaceTag);
        saver.saveValue ("packet", packetTag);
        saver.saveValue ("eomTimeout", eomTimeout);
        saver.saveValue ("alwaysRespond", alwaysRespond);
        saver.saveValue ("idleTimeout", idleTimeout);

        saver.startTag ("interfaces");
        saver.saveValue ("socket", tcpSettings);
        saver.saveValue ("udpsocket", udpSettings);
        saver.saveValue ("serial", serialSettings);
        saver.endTag ("interfaces");
    }

    public Xml.Loader getXmlLoader (Reporter reporter)
    {
        if (reporter == null)
            throw new NullPointerException ("reporter must not be null");
        return new XmlLoader (reporter);
    }

    private class XmlLoader
        extends Xml.AbstractLoader
    {
        private final Reporter reporter;

        XmlLoader (Reporter reporter)
        {
            this.reporter = reporter;
        }

        public Xml.Loader startChild (String tag)
        {
            if (tag.equalsIgnoreCase ("interfaces"))
            {
                return
                    new Xml.AbstractLoader ()
                    {
                        public Xml.Loader startChild (String tag)
                        {
                            if (tag.equalsIgnoreCase ("socket"))
                                return tcpSettings.getXmlLoader ();
                            if (tag.equalsIgnoreCase ("udpsocket"))
                                return udpSettings.getXmlLoader ();
                            if (tag.equalsIgnoreCase ("serial"))
                                return serialSettings.getXmlLoader ();
                            return null;
                        }
                    };
            }

            if (tag.equalsIgnoreCase ("interface"))
            {
                String tags [] = new String []
                    { "socket", "udpsocket", "serial", "tcp", "udp" };

                return new Xml.StringLoader (tags, new Xml.Receiver<String> ()
                {
                    public void receive (String value)
                    {
                        if (value.equals ("socket") || value.equals ("tcp"))
                        {
                            interfaceTag = "tcp";
                            alwaysRespond = true;
                        }
                        if (value.equals ("udpsocket") || value.equals ("udp"))
                        {
                            interfaceTag = "udp";
                            alwaysRespond = true;
                        }
                        else if (value.equals ("serial"))
                        {
                            if (!SerialConnection.isAvailable ())
                            {
                                reporter.warn ("I101",
                                    "Serial comms not available");
                            }
                            else
                            {
                                interfaceTag = "serial";
                                alwaysRespond = false;
                            }
                        }
                    }
                });
            }

            if (tag.equalsIgnoreCase ("packet"))
            {
                return new Xml.StringLoader (PACKET_TYPE_TAGS,
                    new Xml.Receiver<String> ()
                    {
                        public void receive (String value)
                        {
                            if (!value.equals (""))
                                packetTag = value;
                        }
                    });
            }

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

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

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

            return null;
        }

        @Override
        public void end (String value)
        {
            int it = tcpSettings.getIdleTimeout ();
            if (it >= 0)
                idleTimeout = it;
        }

        @Override
        public void cleanup ()
        {
            fireValueChanged ();
        }
    }

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

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

    public void fireValueChanged ()
    {
        listeners.fireValueChanged (this);
    }
}


