
package uk.co.wingpath.gui;

import java.util.*;
import java.util.List;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import uk.co.wingpath.util.*;
import uk.co.wingpath.event.*;
import uk.co.wingpath.event.Event;


public class WIntegerField
    implements WComponent<Integer>
{
    private final WTextField field;
    private int value;
    private int radix;
    private final StatusBar statusBar;
    private final String label;
    private final int minValue;
    private final int maxValue;
    private final IntegerVerifier verifier;

    public WIntegerField (StatusBar statusBar, String label,
        int minValue, int maxValue, int initialValue)
    {
        Event.checkIsEventDispatchThread ();
        this.statusBar = statusBar;
        this.label = label;
        this.minValue = minValue;
        this.maxValue = maxValue;
        radix = 10;
        verifier = new IntegerVerifier ();
        field = new WTextField (statusBar, label);
        setValue (initialValue);
        field.setVerifier (verifier);
        field.addValueListener (new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    value = (int) fromString (field.getValue ());
                }
            });
    }

    public WIntegerField (StatusBar statusBar, String label)
    {
        this (statusBar, label, Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
    }

    public Integer getValue ()
    {
        return value;
    }

    public void setValue (Integer val)
    {
        Event.checkIsEventDispatchThread ();
        if (val < minValue || val > maxValue)
            throw new IllegalArgumentException ("Value out of range: " + val);

        // The order of the next two statements is important - we don't
        // want to fire a ValueEvent.
        value = val;
        field.setValue (toString (value));
    }

    public boolean hasValueChanged (Integer oldValue)
    {
        return field.hasValueChanged (toString (oldValue));
    }

    public boolean checkValue ()
    {
        return field.checkValue ();
    }

    public void setAlignment (int alignment)
    {
        field.setAlignment (alignment);
    }

    public void setEditable (boolean editable)
    {
        field.setEditable (editable);
    }

    public void setRadix (int radix)
    {
        this.radix = radix;
        field.setValue (toString (value));
    }

    private String toString (long n)
    {
        switch (radix)
        {
        case 2:
            String s = Long.toBinaryString (n);
            while ((1L << s.length ()) <= maxValue)
                s = "0" + s;
            return s;

        case 8:
            return Long.toOctalString (n);

        case 16:
            return Long.toHexString (n);

        case 10:
        case 0:
            return Long.toString (n);

        default:
            throw new IllegalArgumentException (
                "Invalid radix " + radix);
        }
    }

    private long fromString (String str)
        throws NumberFormatException
    {
        return Long.parseLong (str.trim (), radix);
    }

    private class IntegerVerifier
        implements Verifier
    {
        private String rangeMessage ()
        {
            String radixStr = "a";
            switch (radix)
            {
            case 2:
                radixStr = "a binary";
                break;
            case 8:
                radixStr = "an octal";
                break;
            case 10:
                radixStr = "a decimal";
                break;
            case 0:
                radixStr = "an unsigned decimal";
                break;
            case 16:
                radixStr = "a hexadecimal";
                break;
            }
            return "Must be " + radixStr + " integer in the range " +
                WIntegerField.this.toString (minValue) +
                " to " + WIntegerField.this.toString (maxValue);
        }

        private void reportError (String msg)
        {
            if (label != null)
                msg = label + ": " + msg;
            statusBar.showError (msg);
        }

        public String verify (String str, boolean isChanging)
        {
            if (isChanging)
            {
                if (statusBar != null)
                    statusBar.clear ();
                return str;
            }
            if (str.equals (""))
            {
                reportError ("Value missing");
                return null;
            }

            try
            {
                long n = fromString (str);
                if (n < minValue || n > maxValue)
                {
                    reportError (rangeMessage ());
                    return null;
                }
                if (statusBar != null)
                    statusBar.clear ();
                return WIntegerField.this.toString (n);
            }
            catch (NumberFormatException e)
            {
                reportError (rangeMessage ());
                return null;
            }
        }
    }

    public void setMirror (MirrorField mirror)
    {
        field.setMirror (mirror);
    }

    public void setBackground (Color bg)
    {
        field.setBackground (bg);
    }

    public void requestFocusInWindow ()
    {
        field.requestFocusInWindow ();
    }

    public void setToolTipText (String text)
    {
        field.setToolTipText (text);
    }

    public void setToolTipText (String [] text)
    {
        field.setToolTipText (text);
    }

    public void setMnemonic (int mnemonic)
    {
        field.setMnemonic (mnemonic);
    }

    public void setMnemonic (int mnemonic, int index)
    {
        field.setMnemonic (mnemonic, index);
    }

    public void setEnabled (boolean enabled)
    {
        field.setEnabled (enabled);
    }

    public void setWidth (int width)
    {
        field.setWidth (width);
    }

    public void setWidthChars (int width)
    {
        field.setWidthChars (width);
    }

    public JComponent getComponent ()
    {
        return field.getComponent ();
    }

    public JLabel getLabel ()
    {
        return field.getLabel ();
    }

    public void addValueListener (ValueListener l)
    {
        field.addValueListener (l);
    }

    public void removeValueListener (ValueListener l)
    {
        field.removeValueListener (l);
    }

}

