package util.swing;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
  * A {@link JTextField} that will allow only int values within a certain range to be entered.
  *
  * <p><b>Note:</b> This class is not meant to be serialized!</p>
  *
  * @author Steffen Zschaler
  * @version 2.0 28/07/1999
  * @since v2.0
  */
public class JIntInput extends JTextField {

  /**
    * The current value observer. The current value can always be found as the first element of the array.
    *
    * @serial This class is not meant to be serialized!
    */
  protected int[] m_anValue;

  /**
    * The default value of the input field.
    *
    * @serial This class is not meant to be serialized!
    */
  protected int m_nDefault = 0;

  /**
    * The minimum value.
    *
    * @serial This class is not meant to be serialized!
    */
  protected int m_nMinimum;

  /**
    * The maximum value.
    *
    * @serial This class is not meant to be serialized!
    */
  protected int m_nMaximum;

  /**
    * Create a new JIntInput. The minimum and maximum values default to {@link java.lang.Integer#MIN_VALUE} and
    * {@link java.lang.Integer#MAX_VALUE}, resp.
    *
    * @param anValue the value observer the current value of the input field can be found as the first element
    * of the array at any time.
    * @param nDefault the default value of the input line.
    */
  public JIntInput (int[] anValue, int nDefault) {
    this (anValue, nDefault, Integer.MIN_VALUE, Integer.MAX_VALUE);
  }

  /**
    * Create a new JIntInput.
    *
    * @param anValue the value observer the current value of the input field can be found as the first element
    * of the array at any time.
    * @param nDefault the default value of the input line.
    * @param nMinimum the minimum input value.
    * @param nMaximum the maximum input value.
    */
  public JIntInput (int[] anValue,
                    int nDefault,
                    int nMinimum,
                    int nMaximum) {

    super ("0");
    // The value set here can be anything, because we set the actual value at the end of this constructor
    // This is done, so that everything is properly initialized when the setText method is called.

    m_anValue = anValue;
    m_nMinimum = nMinimum;
    m_nMaximum = nMaximum;
    m_nDefault = ((nDefault >= m_nMinimum)?
                  ((nDefault <= m_nMaximum)?
                   (nDefault):
                   (m_nMaximum)):
                  (m_nMinimum));
    m_anValue[0] = m_nDefault;

    getDocument().addDocumentListener (new DocumentListener() {
      public void changedUpdate (DocumentEvent e) {
        performUpdate();
      }

      public void insertUpdate (DocumentEvent e) {
        performUpdate();
      }

      public void removeUpdate (DocumentEvent e) {
        performUpdate();
      }

      private void performUpdate() {
        String sText = getText();
        if (sText == "") {
          sText = Integer.toString (m_nDefault);
        }

        try {
          m_anValue[0] = Integer.parseInt (sText);
        }
        catch (NumberFormatException nfe) {}
      }
    });

    setHorizontalAlignment (RIGHT);

    // we set the text again, because when this is done in the super class constructor, the minimum and maximum values
    // have not yet been set correctly.
    setText (Integer.toString (m_nDefault));
  }

  /**
    * Create and return the input fields document. This will create a document that allows only int values
    * within the current range to be entered.
    */
  protected Document createDefaultModel() {
    return new PlainDocument() {
      private boolean m_fDidSpecialRemove = false;

      public void insertString(int offs, String str, AttributeSet a)
        throws BadLocationException {

        if (m_fDidSpecialRemove) {
          if (getLength() > 0) {
            super.remove (0, getLength());
          }

          offs = 0;
          m_fDidSpecialRemove = false;
        }

        if (str == null) {
          return;
        }

        String sText = getText (0, getLength());

        if (sText == null) {
          sText = "";
          offs = 0;
        }

        sText = new StringBuffer (sText).insert (offs, str).toString();

        try {
          int nCounter = Integer.parseInt (sText);

          if (nCounter < m_nMinimum) {
            return;
          }

          if (nCounter > m_nMaximum) {
            return;
          }

          super.insertString (offs, str, a);
        }
        catch (NumberFormatException nfe) {
          if (getLength() == 0) {
            super.insertString (0, Integer.toString (m_nDefault), a);
          }
        }
      }

      public void remove (int offs, int len)
        throws BadLocationException {

        String sText = getText (0, getLength());

        if (sText == null) {
          return;
        }

        sText = new StringBuffer (sText).delete (offs, offs + len).toString();

        if (sText.equals ("")) {
          sText = null;
        }

        try {
          int nCounter = 0;

          if (sText != null) {
            nCounter = Integer.parseInt (sText);
          }

          if ((nCounter < m_nMinimum) ||
              (nCounter > m_nMaximum) ||
              (sText == null)) {
            super.remove (0, getLength());
            super.insertString (0, Integer.toString (m_nDefault), null);

            m_fDidSpecialRemove = true;
          }
          else {
            super.remove (offs, len);

            m_fDidSpecialRemove = false;
          }
        }
        catch (NumberFormatException nfe) {
          m_fDidSpecialRemove = false;
        }
      }
    };
  }
}