package com.xith.java3d.overlay;

import com.xith.java3d.overlay.*;
import javax.media.j3d.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.awt.event.*;
import java.text.*;
import java.awt.font.*;
import javax.vecmath.*;

public class LabelOverlay extends OverlayBase {
    protected AttributedString text;

    protected int visibleLength = 0;
    protected int textLength = 0;
    protected int typingDelta = 0;

    protected Font font;
    protected Color color;
    TypingBehavior typer;
    boolean typing = false;
    
    public LabelOverlay( Canvas3D canvas, Rectangle space ) {
	this(canvas, space, "");
    }
    
    public LabelOverlay( Canvas3D canvas, Rectangle space, String text ) {
	this(canvas, space, text, new Font("Helvetica", Font.BOLD, 14), Color.red, null);
    }
    
    public LabelOverlay( Canvas3D canvas, Rectangle space, String text, Font font, Color color ) {
	this(canvas, space, text, font, color, null);
    }
    
    public LabelOverlay( Canvas3D canvas, Rectangle space,
			 String text, Font font, Color color,
			 UpdateManager manager ) {
	super(canvas, space, manager);
	this.font = font;
	this.color = color;
	setText(text);
    }
    
    public LabelOverlay( Canvas3D canvas, Rectangle space, AttributedString text ) {
	this(canvas, space, text, (UpdateManager)null);
    }
    
    public LabelOverlay( Canvas3D canvas, Rectangle space,
			 AttributedString text, UpdateManager manager ) {
	super(canvas, space, manager);
	setText(text);
    }

    protected void initialize() {
	typer = new TypingBehavior();
	typer.setSchedulingBounds(new BoundingSphere());
	getRoot().addChild(typer);
	setBackgroundColor(new Color(0, 0, 0, 0));
    }   

    public void paint(Graphics2D g) {
	if (text != null) {
	    synchronized(text) {
		AttributedCharacterIterator characterIterator = text.getIterator(null, 0, visibleLength);
		if(characterIterator.getEndIndex() > 0) {
		    TextLayout textLayout = new TextLayout(characterIterator, g.getFontRenderContext());
		    textLayout.draw(g, 0, 3 * getBounds().height / 4);
		}
	    }
	}
    }

    public void setColor(Color color) {
	if(!this.color.equals(color)) {
	    this.color = color;
	    repaint();
	}
    }

    public void setFont(Font font) {
	if(!this.font.equals(font)) {
	    this.font = font;
	    repaint();
	}
    }

    public void setText(String text) {
	setText(text, text.length());
    }
    
    public void setText(String text, int typingDelta) {
	setText(createAttributedString(text, font, color), typingDelta);
    }

    public void setText(AttributedString text) {
	if(text != null) {
	    setText(text, text.getIterator().getEndIndex() + 1);
	}
    }

    public void setText(AttributedString text, int typingDelta) {
	if(this.text != text) {
	    synchronized(text) {
		this.text = text;
		textLength = text.getIterator().getEndIndex();
		visibleLength = 0;
		this.typingDelta = typingDelta;
	    }
	    typer.type();
	}
    }

    public void backspace(int numCharacters) {
	visibleLength = Math.max(0, visibleLength - numCharacters);
    }

    Object typingBlock = new Object();

    protected void updateTyping() {
	visibleLength = Math.min(textLength, visibleLength + typingDelta);
	repaint();
	typing = visibleLength < textLength;
	if (!typing) {
	    synchronized (typingBlock) {
		typingBlock.notifyAll();
	    }
	}
    }

    public void waitForTyping() {
	while (typing) {
	    synchronized (typingBlock) {
		try {
		    typingBlock.wait();
		} catch(InterruptedException e) {
		}
	    }
	}
    }

    public void setTypingSpeed(long delayInMilliseconds) {
	typer.setTypingSpeed(delayInMilliseconds);
    }

    public int getVisibleLength() {
	return visibleLength;
    }

    public boolean isTyping() {
	return typing;
    }

    public static AttributedString createAttributedString(String text, Font font, Color color) {
	AttributedString attributedString = new AttributedString(text);
	if (text.length() > 0) { 
	    attributedString.addAttribute(TextAttribute.FONT, font);
	    attributedString.addAttribute(TextAttribute.FOREGROUND, color);
	}
	return attributedString;
    }
    
    class TypingBehavior extends Behavior {
	int START_TYPING = 1;
	WakeupOnBehaviorPost postWakeup = new WakeupOnBehaviorPost(this, START_TYPING);
	WakeupOnElapsedTime timeWakeup = new WakeupOnElapsedTime(100);

	public void initialize() {
	    wakeupOn(new WakeupOnActivation());
	}

	public void type() {
	    postId(START_TYPING);
	    typing = true;
	}

	public void processStimulus(Enumeration criteria) {
	    updateTyping();
	    if (typing) {
		wakeupOn(timeWakeup);
	    } else {
		wakeupOn(postWakeup);
	    }
	}

	public void setTypingSpeed(long speed) {
	    timeWakeup = new WakeupOnElapsedTime(speed);
	}
    }
}
