
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.event.Event;
import uk.co.wingpath.util.*;
import uk.co.wingpath.modbus.*;
import uk.co.wingpath.gui.*;
import uk.co.wingpath.xml.*;

public class AddressSettings
    implements Xml.Savable
{
    private static final int MIN_BASE = 0;
    private static final int MAX_BASE = Integer.MAX_VALUE;
    private static final int MIN_SIZE = 0;
    private static final int MAX_SIZE = 0x10000;

    private AddressMap addressMap;
    private final ValueEventSource listeners;

    public AddressSettings ()
    {
        addressMap = new AddressMap ();
        listeners = new ValueEventSource ();
    }

    public AddressMap getAddressMap ()
    {
        return addressMap.clone ();
    }

    public void setAddressMap (AddressMap addressMap)
    {
        if (!addressMap.equals (this.addressMap))
        {
            this.addressMap = addressMap.clone ();
            listeners.fireValueChanged (this);
        }
    }

    private void saveMap (String tag, AddressMap.Map map, Xml.Saver saver)
        throws IOException
    {
        saver.startTag (tag);
        saver.saveValue ("base", map.getBase ());
        saver.saveValue ("size", map.getSize ());
        saver.endTag (tag);
    }

    public void save (Xml.Saver saver)
        throws IOException
    {
        saveMap ("holding_register", addressMap.getHoldingRegisterMap (),
            saver);
        saveMap ("input_register", addressMap.getInputRegisterMap (), saver);
        saveMap ("coil", addressMap.getCoilMap (), saver);
        saveMap ("discrete_input", addressMap.getDiscreteInputMap (), saver);
        saver.saveValue ("exception_status",
            addressMap.getExceptionStatusRegister ());
        saver.saveValue ("diagnostic", addressMap.getDiagnosticRegister ());
        saver.saveValue ("byte_swap", addressMap.getByteSwap ());
        saver.saveValue ("bit_reverse", addressMap.getBitReverse ());
    }

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

    private class XmlLoader
        extends Xml.AbstractLoader
    {
        private AddressMap addressMap = new AddressMap ();

        @Override
        public Xml.Loader startChild (String tag)
        {
            if (tag.equalsIgnoreCase ("holding_register"))
                return new MapLoader (addressMap.getHoldingRegisterMap ());
            if (tag.equalsIgnoreCase ("input_register"))
                return new MapLoader (addressMap.getInputRegisterMap ());
            if (tag.equalsIgnoreCase ("coil"))
                return new MapLoader (addressMap.getCoilMap ());
            if (tag.equalsIgnoreCase ("discrete_input"))
                return new MapLoader (addressMap.getDiscreteInputMap ());
            if (tag.equalsIgnoreCase ("exception_status"))
            {
                return new Xml.IntegerLoader (-1, Integer.MAX_VALUE,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            addressMap.setExceptionStatusRegister (value);
                        }
                    });
            }
            if (tag.equalsIgnoreCase ("diagnostic"))
            {
                return new Xml.IntegerLoader (-1, Integer.MAX_VALUE,
                    new Xml.Receiver<Integer> ()
                    {
                        public void receive (Integer value)
                        {
                            addressMap.setDiagnosticRegister (value);
                        }
                    });
            }
            if (tag.equalsIgnoreCase ("byte_swap"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        addressMap.setByteSwap (value);
                    }
                });
            }
            if (tag.equalsIgnoreCase ("bit_reverse"))
            {
                return new Xml.BooleanLoader (new Xml.Receiver<Boolean> ()
                {
                    public void receive (Boolean value)
                    {
                        addressMap.setBitReverse (value);
                    }
                });
            }

            return null;
        }

        @Override
        public void end (String value)
        {
            AddressSettings.this.addressMap = addressMap;
            listeners.fireValueChanged (this);
        }

        private class MapLoader
            extends Xml.AbstractLoader
        {
            private final AddressMap.Map map;

            MapLoader (AddressMap.Map map)
            {
                this.map = map;
            }

            @Override
            public Xml.Loader startChild (String tag)
            {
                if (tag.equalsIgnoreCase ("base"))
                {
                    return new Xml.IntegerLoader (
                        MIN_BASE, MAX_BASE,
                        new Xml.Receiver<Integer> ()
                        {
                            public void receive (Integer value)
                            {
                                map.setBase (value);
                            }
                        });
                }

                if (tag.equalsIgnoreCase ("size"))
                {
                    return new Xml.IntegerLoader (
                        MIN_SIZE, MAX_SIZE,
                        new Xml.Receiver<Integer> ()
                        {
                            public void receive (Integer value)
                            {
                                map.setSize (value);
                            }
                        });
                }

                return null;
            }

            @Override
            public void end (String value)
            {
            }
        }
    }

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

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

