// Copyright 2006 by Basil Vandegriend.  All rights reserved.

package com.basilv.core.richui;

import java.awt.Color;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;

import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.text.Document;

/**
 * Extend JTextField to provide validation behavior.
 * An input verifier and focus listener are used to implement this behavior.
 */
public class ValidatingTextField extends JTextField {

	private static final long serialVersionUID = 1L;

    private boolean isTextValid = false;

    private String validationRegexPattern;
    
    private Color invalidBackgroundColor = Color.RED;
    private String invalidTooltipText = "Invalid value";
    
    private Color validBackgroundColor;
    private String validTooltipText;

    
    /**
     * Create an instance with the specified Document and number of columns as per JTextField(int).
     *
     * @param document the document to use for this textfield. Cannot be null.
     * @param columns  the number of columns to use to calculate 
     * the preferred width; if columns is set to zero, the
     * preferred width will be whatever naturally results from
     * the component implementation
     * @param validationPattern  regular expression pattern to use 
     * to determine if the field's text is valid.
     */ 
    public ValidatingTextField(Document document, int columns, String validationPattern) {
        this(columns, validationPattern);
        
        assert document != null;
        setDocument(document);
    }

    /**
     * Create an instance with the specified number of columns as per JTextField(int).
     *
     * @param columns  the number of columns to use to calculate 
     *   the preferred width; if columns is set to zero, the
     *   preferred width will be whatever naturally results from
     *   the component implementation
     *   
     * @param validationPattern regular expression pattern to use 
     * to determine if the field's text is valid.
     */ 
    public ValidatingTextField(int columns, String validationPattern) {
        super(columns);
        
        assert validationPattern != null;
        
        this.validationRegexPattern = validationPattern;

        // When gains focus, stop indicating error state.
        this.addFocusListener(new FocusAdapter() {

            @Override
            public void focusGained(FocusEvent e) {
                if (!isTextValid) {
                    displayValid();
                }
            }

        });
        
        this.setInputVerifier(new InputVerifier() {

            @Override
            public boolean shouldYieldFocus(JComponent input) {
                isTextValid = verify(input);
                
                if (!isTextValid) {
                    displayInvalid();
                }
                
                // Always allow focus to change.
                return true;
            }

            @Override
            public boolean verify(JComponent input) {
                assert input == ValidatingTextField.this;
                String text = getText();
                if (text == null) {
                    text = "";
                }
                return text.matches(validationRegexPattern);
            }
            
            
        });
        
        validBackgroundColor = getBackground();
        validTooltipText = getToolTipText();

        // Update initial state
        getInputVerifier().shouldYieldFocus(this);
        
        // If invalid, we don't want to display the invalid state until 
        // after the user enters and leaves the field.
        displayValid();
    }

    /**
     * Return whether this field's text is valid or not.
     * @return Returns true if valid, false otherwise.
     */
    public boolean isTextValid() {
        return isTextValid;
    }

    /**
     * Set the tooltip text to display when the field is invalid.
     * @param invalidTooltipText May be null.
     */
    public void setInvalidTooltipText(String invalidTooltipText) {
        this.invalidTooltipText = invalidTooltipText;
    }

    private void displayInvalid() {
        setBackground(invalidBackgroundColor);
        setToolTipText(invalidTooltipText);
    }

    private void displayValid() {
        setBackground(validBackgroundColor);
        setToolTipText(validTooltipText);
    }
    
    
    
}
