
package uk.co.wingpath.modbusgui;

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

public class GeneralSettings
    implements Xml.Savable, TracerFactory
{
    public static final int MIN_SLAVE_ID = 0;
    public static final int MAX_SLAVE_ID = 255;
    public static final int MIN_REQUEST_DELAY = 0;
    public static final int MAX_REQUEST_DELAY = Integer.MAX_VALUE;
    public static final int MIN_RESPONSE_DELAY = 0;
    public static final int MAX_RESPONSE_DELAY = Integer.MAX_VALUE;
    public static final int MIN_PASS_DELAY = 0;
    public static final int MAX_PASS_DELAY = Integer.MAX_VALUE;
    public static final int MIN_RETRIES = 0;
    public static final int MAX_RETRIES = 100;
    public static final int MIN_REPLY_TIMEOUT = 0;
    public static final int MAX_REPLY_TIMEOUT = Integer.MAX_VALUE;

    private final Product product;

    private final Variable<Backend.Mode> mode;
    private final Variable<Boolean> poll;
    private final Variable<Integer> slaveId;
    private final Variable<Boolean> tracingSetting;
    private final Variable<Boolean> intTraceSetting;
    private final Variable<Boolean> rawTraceSetting;
    private final Variable<Boolean> readTraceSetting;
    private final Variable<Boolean> writeTraceSetting;
    private final Variable<Boolean> singleValue;
    private final Variable<Boolean> singleWrite;
    private final Variable<Boolean> continuePolling;
    private final Variable<Integer> requestDelay;
    private final Variable<Integer> responseDelay;
    private final Variable<Integer> passDelay;
    private final Variable<Integer> retries;
    private final Variable<Integer> replyTimeout;
    private final Variable<Integer> maxPdu;
    private final Variable<Boolean> checkCountLimits;
    private final Variable<Boolean> strictChecking;
    private final Variable<Boolean> allowLongMessages;
    private final Variable<Boolean> useBrowser;
    private final LinkedList<Tracer> tracers;

    public GeneralSettings (Product product)
    {
        this.product = product;

        tracers = new LinkedList<Tracer> ();
        tracingSetting = new SimpleVariable<Boolean> (false);
        rawTraceSetting = new SimpleVariable<Boolean> (false);
        intTraceSetting = new SimpleVariable<Boolean> (false);
        readTraceSetting = new SimpleVariable<Boolean> (false);
        writeTraceSetting = new SimpleVariable<Boolean> (false);

        ValueListener traceListener = new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    for (Tracer tracer : tracers)
                    {
                        enableTracer (tracer);
                    }
                }
            };
        tracingSetting.addValueListener (traceListener);
        rawTraceSetting.addValueListener (traceListener);
        intTraceSetting.addValueListener (traceListener);
        readTraceSetting.addValueListener (traceListener);
        writeTraceSetting.addValueListener (traceListener);

        slaveId = new SimpleVariable<Integer> (1);
        requestDelay = new SimpleVariable<Integer> (0);
        if (product.isTester ())
        {
            poll = null;
            singleValue = null;
            singleWrite = null;
            continuePolling = null;
            passDelay = null;
        }
        else
        {
            poll = new SimpleVariable<Boolean> (false);
            singleValue = new SimpleVariable<Boolean> (false);
            singleWrite = new SimpleVariable<Boolean> (false);
            continuePolling = new SimpleVariable<Boolean> (false);
            passDelay = new SimpleVariable<Integer> (500);
        }
        if (product.hasSlaveMode ())
        {
            mode = new SimpleVariable<Backend.Mode> (Backend.Mode.master);
            responseDelay = new SimpleVariable<Integer> (0);
        }
        else
        {
            mode = null;
            responseDelay = null;
        }
        retries = new SimpleVariable<Integer> (2);
        replyTimeout = new SimpleVariable<Integer> (1000);
        maxPdu = new SimpleVariable<Integer> (Modbus.MAX_PDU_SIZE);
        checkCountLimits = new SimpleVariable<Boolean> (true);
        strictChecking = new SimpleVariable<Boolean> (true);
        allowLongMessages = new SimpleVariable<Boolean> (false);
        useBrowser = new SimpleVariable<Boolean> (false);
    }

    private void enableTracer (Tracer tracer)
    {
        tracer.setTracing (tracingSetting.getValue ());
        tracer.setRawTraceEnabled (rawTraceSetting.getValue ());
        tracer.setIntTraceEnabled (intTraceSetting.getValue ());
        tracer.setReadTraceEnabled (readTraceSetting.getValue ());
        tracer.setWriteTraceEnabled (writeTraceSetting.getValue ());
    }

    public Tracer createTracer (Reporter reporter)
    {
        Tracer tracer = new Tracer (reporter);
        tracers.add (tracer);
        enableTracer (tracer);
        return tracer;
    }

    public void deleteTracer (Tracer tracer)
    {
        tracers.remove (tracer);
    }

    public Variable<Integer> getSlaveId ()
    {
        return slaveId;
    }

    public Variable<Backend.Mode> getMode ()
    {
        assert product.hasSlaveMode ();
        return mode;
    }

    public Variable<Boolean> getTracingSetting ()
    {
        return tracingSetting;
    }

    public Variable<Boolean> getRawTraceSetting ()
    {
        return rawTraceSetting;
    }

    public Variable<Boolean> getIntTraceSetting ()
    {
        return intTraceSetting;
    }

    public Variable<Boolean> getReadTraceSetting ()
    {
        return readTraceSetting;
    }

    public Variable<Boolean> getWriteTraceSetting ()
    {
        return writeTraceSetting;
    }

    public Variable<Boolean> getSingleValue ()
    {
        return singleValue;
    }

    public Variable<Boolean> getSingleWrite ()
    {
        return singleWrite;
    }

    public Variable<Boolean> getPoll ()
    {
        return poll;
    }

    public Variable<Boolean> getContinuePolling ()
    {
        return continuePolling;
    }

    public Variable<Integer> getRequestDelay ()
    {
        return requestDelay;
    }

    public Variable<Integer> getResponseDelay ()
    {
        return responseDelay;
    }

    public Variable<Integer> getPassDelay ()
    {
        return passDelay;
    }

    public Variable<Integer> getRetries ()
    {
        return retries;
    }

    public Variable<Integer> getReplyTimeout ()
    {
        return replyTimeout;
    }

    public Variable<Integer> getMaxPdu ()
    {
        return maxPdu;
    }

    public Variable<Boolean> getCheckCountLimits ()
    {
        return checkCountLimits;
    }

    public Variable<Boolean> getStrictChecking ()
    {
        return strictChecking;
    }

    public Variable<Boolean> getAllowLongMessages ()
    {
        return allowLongMessages;
    }

    public Variable<Boolean> getUseBrowser ()
    {
        return useBrowser;
    }

    public void save (Xml.Saver saver)
        throws IOException
    {
        if (mode != null)
            saver.saveValue ("mode", mode.getValue ().toString ());
        saver.saveValue ("slaveid", slaveId.getValue ());
        saver.saveValue ("tracing", tracingSetting.getValue ());
        saver.saveValue ("rawtracing", rawTraceSetting.getValue ());
        saver.saveValue ("inttracing", intTraceSetting.getValue ());
        if (!product.isTester ())
        {
            saver.saveValue ("readtracing", readTraceSetting.getValue ());
            saver.saveValue ("writetracing", writeTraceSetting.getValue ());
        }
        if (poll != null)
            saver.saveValue ("poll", poll.getValue ());
        if (singleValue != null)
            saver.saveValue ("singlevalue", singleValue.getValue ());
        if (singleWrite != null)
            saver.saveValue ("singlewrite", singleWrite.getValue ());
        if (continuePolling != null)
            saver.saveValue ("continuepolling", continuePolling.getValue ());
        saver.saveValue ("requestdelay", requestDelay.getValue ());
        if (responseDelay != null)
            saver.saveValue ("responsedelay", responseDelay.getValue ());
        if (passDelay != null)
            saver.saveValue ("passdelay", passDelay.getValue ());
        saver.saveValue ("retries", retries.getValue ());
        saver.saveValue ("replytimeout", replyTimeout.getValue ());
        saver.saveValue ("maxpdu", maxPdu.getValue ());
        saver.saveValue ("checkcountlimits", checkCountLimits.getValue ());
        saver.saveValue ("strictchecking", strictChecking.getValue ());
        saver.saveValue ("allowlongmessages", allowLongMessages.getValue ());
        saver.saveValue ("usebrowser", useBrowser.getValue ());
    }

    public Xml.Loader getXmlLoader ()
    {
        return new XmlLoader ();
    }

    private class XmlLoader
        extends Xml.AbstractLoader
    {
        @Override
        public Xml.Loader startChild (String tag)
        {
            if (mode != null && tag.equalsIgnoreCase ("mode"))
            {
                return new Xml.StringLoader (new Xml.Receiver<String> ()
                    {
                        public void receive (String value)
                            throws ValueException
                        {
                            try
                            {
                                if (value.equals ("master_manual"))
                                {
                                    // Backwards compatibility - old versions
                                    // had "master" and "master_manual"
                                    // modes, and no "poll" flag.

                                    mode.setValue (Backend.Mode.master);
                                    poll.setValue (false);
                                }
                                else
                                {
                                    mode.setValue (
                                        Backend.Mode.valueOf (value));

                                    // Backwards compatibility - old versions
                                    // used "master" to mean automatic
                                    // polling. New versions will have "poll"
                                    // flag, which will override the following.

                                    poll.setValue (value.equals ("master"));
                                }
                            }
                            catch (IllegalArgumentException e)
                            {
                                throw new ValueException (e.getMessage ());
                            }
                        }
                    });
            }

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

            if (tag.equalsIgnoreCase ("tracing"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        tracingSetting.setValue (value);
                        intTraceSetting.setValue (value);
                    }
                });
            }

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

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

            if (tag.equalsIgnoreCase ("logging"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        readTraceSetting.setValue (value);
                        writeTraceSetting.setValue (value);
                    }
                });
            }

            if (tag.equalsIgnoreCase ("logreads") || tag.equalsIgnoreCase ("readtracing"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        readTraceSetting.setValue (value);
                    }
                });
            }

            if (tag.equalsIgnoreCase ("logwrites") || tag.equalsIgnoreCase ("writetracing"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        writeTraceSetting.setValue (value);
                    }
                });
            }

            if (poll != null && tag.equalsIgnoreCase ("poll"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        poll.setValue (value);
                    }
                });
            }

            if (singleValue != null && tag.equalsIgnoreCase ("singlevalue"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        singleValue.setValue (value);
                    }
                });
            }

            if (singleWrite != null && tag.equalsIgnoreCase ("singlewrite"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        singleWrite.setValue (value);
                    }
                });
            }

            if (continuePolling != null && tag.equalsIgnoreCase ("continuepolling"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        continuePolling.setValue (value);
                    }
                });
            }

            if (tag.equalsIgnoreCase ("requestdelay"))
            {
                return new Xml.IntegerLoader (
                    MIN_REQUEST_DELAY, MAX_REQUEST_DELAY,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            requestDelay.setValue (value);
                        }
                    });
            }

            if (responseDelay != null && tag.equalsIgnoreCase ("responsedelay"))
            {
                return new Xml.IntegerLoader (
                    MIN_RESPONSE_DELAY, MAX_RESPONSE_DELAY,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            responseDelay.setValue (value);
                        }
                    });
            }

            if (passDelay != null && tag.equalsIgnoreCase ("passdelay"))
            {
                return new Xml.IntegerLoader (
                    MIN_PASS_DELAY, MAX_PASS_DELAY,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            passDelay.setValue (value);
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("retries"))
            {
                return new Xml.IntegerLoader (
                    MIN_RETRIES, MAX_RETRIES,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            retries.setValue (value);
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("replytimeout"))
            {
                return new Xml.IntegerLoader (
                    MIN_REPLY_TIMEOUT, MAX_REPLY_TIMEOUT,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            replyTimeout.setValue (value);
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("maxpdu"))
            {
                return new Xml.IntegerLoader (
                    Modbus.MIN_PDU_SIZE, Modbus.MAX_IMP_PDU_SIZE,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            maxPdu.setValue (value);
                        }
                    });
            }

            if (tag.equalsIgnoreCase ("checkcountlimits") ||
                tag.equalsIgnoreCase ("enforcecountlimits"))
            {
                return new Xml.BooleanLoader (
                    new Xml.Receiver<Boolean> ()
                    {
                        public void receive (Boolean value)
                        {
                            checkCountLimits.setValue (value);
                        }
                    });
            }

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

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

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

            return null;
        }

        @Override
        public void end (String value)
            throws ValueException
        {
            if (!value.equals (""))
                throw new ValueException ("Value not allowed");
        }
    }
}


