import java.awt.Font;
import java.awt.Color;

import java.awt.Shape;
import java.awt.Graphics;

import javax.swing.event.DocumentEvent;

import javax.swing.text.Element;
import javax.swing.text.AttributeSet;
import javax.swing.text.GlyphView;
import javax.swing.text.ViewFactory;

import javax.swing.text.StyleConstants;

// Lazy initialization seems to be done in LabelView because FlowView
// re-parents the leaf views again and again without any
// attribute change. Done similar here.
public class TextView
    extends GlyphView
{
    private Font font;
    private Color foreground;
    private Color background;
    
    private short flags;

    private static final int
        INVALID = 1,
        UNDERLINE = 2,
        STRIKETHROUGH = 4,
        SUPERSCRIPT = 8,
        SUBSCRIPT = 16;


    public TextView(Element e)
    {
        super(e);

        setFlag(INVALID);
    }


    protected void setUnderline(boolean value) 
    {
        setFlag(UNDERLINE, value);
    }

    protected void setStrikeThrough(boolean value)
    {
        setFlag(STRIKETHROUGH, value);
    }

    protected void setSuperscript(boolean value)
    {
        setFlag(SUPERSCRIPT, value);
    }

    protected void setSubscript(boolean value)
    {
        setFlag(SUBSCRIPT, value);
    }

    protected void setFont(Font f)
    {
        font = f;
    }

    protected void setBackground(Color c)
    {
        background = c;
    }

    protected void setForeground(Color c)
    {
        foreground = c;
    }


    /** font by StyleConstants, not by StyledDocument.getFont(). Override if you really want that. */
    protected Font font(AttributeSet a)
    {
        return new Font(StyleConstants.getFontFamily(a), (StyleConstants.isBold(a) ? Font.BOLD : Font.PLAIN) | (StyleConstants.isItalic(a) ? Font.ITALIC : Font.PLAIN), StyleConstants.getFontSize(a));
    }

    /** Override to change any properties afterwards. Also set custom properties there, use validate() in the accessor. */
    protected void setPropertiesFromAttributes()
    {
        AttributeSet a = getAttributes();

        setForeground(StyleConstants.getForeground(a));
        setBackground(a.isDefined(StyleConstants.Background) ? StyleConstants.getBackground(a) : null);
        setFont(font(a));
        setUnderline(StyleConstants.isUnderline(a));
        setStrikeThrough(StyleConstants.isStrikeThrough(a));
        setSuperscript(StyleConstants.isSuperscript(a));
        setSubscript(StyleConstants.isSubscript(a));
    }

    public final Color getBackground()
    {
        validate();
        return background;
    }

    public final Color getForeground()
    {
        validate();
        return foreground;
    }

    public final Font getFont()
    {
        validate();
        return font;
    }

    public final boolean isUnderline()
    {
        validate();
        return flag(UNDERLINE);
    }

    public final boolean isStrikeThrough()
    {
        validate();
        return flag(STRIKETHROUGH);
    }

    public final boolean isSubscript()
    {
        validate();
        return flag(SUBSCRIPT);
    }

    public final boolean isSuperscript()
    {
        validate();
        return flag(SUPERSCRIPT);
    }



    protected final void validate()
    {
        if (isInvalid())
        {
            // do this before so that getXXX can be called
            // from setPropertiesFromAttributes
            clearFlag(INVALID);

            setPropertiesFromAttributes();
        }
    }

    protected void invalidate()
    {
        setFlag(INVALID);
    }

    protected final boolean isInvalid()
    {
        return flag(INVALID);
    }



    public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
    {
        invalidate();
        super.changedUpdate(e, a, f);
    }


    private void setFlag(int flag)
    {
        flags |= flag;
    }

    private void clearFlag(int flag)
    {
        flags &= ~flag;
    }

    private boolean flag(int flag)
    {
        return (flags & flag) != 0;
    }

    private void setFlag(int flag, boolean value)
    {
        if (value)
            setFlag(flag);
        else
            clearFlag(flag);
    }
}


