/*
 *      @(#)ViewPlatformAWTBehavior.java 1.2 01/02/08 09:08:48
 *
 * Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */


import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Cursor;
import javax.swing.SwingUtilities;
import java.util.ArrayList;

import javax.media.j3d.Behavior;
import javax.media.j3d.WakeupOnBehaviorPost;
import javax.media.j3d.WakeupOnElapsedFrames;
import javax.media.j3d.WakeupOr;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupCondition;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.Transform3D;
import javax.media.j3d.View;
import javax.media.j3d.Canvas3D;

import javax.vecmath.Vector3f;


/**
 * Abstract class which implements much of the event tracking and
 * state updating in a thread safe manner.
 *
 * AWT Events are captured and placed in a queue.
 *
 * While there are pending events or motion the behavior will wake
 * up every frame, call processAWTEvents and integrateTransforms.
 *
 * @since Java 3D 1.2.1
 *
 *
 */
public abstract class ViewAWTBehavior extends Behavior
                                                 implements MouseListener, MouseMotionListener, KeyListener {

    private final static boolean DEBUG = false;

    /**
     * Behavior PostId used in this behavior
     */
    protected final static int POST_ID = 9998;

    /**
     * The different criterion for the behavior to wakeup
     */
    protected WakeupOnElapsedFrames frameWakeup;

    /**
     * The Or of the different criterion for the behavior to wakeup
     */
    protected WakeupOnBehaviorPost postWakeup;

    /**
     * The target Transform3D for this behavior
     */
    protected Transform3D targetTransform;

    /**
     * Boolean for whether the mouse is in motion
     */
    protected boolean motion = false;

    /**
     * Flag indicating Behavior should listen for Mouse Events
     */
    public final static int MOUSE_LISTENER = 0x01;

    /**
     * Flag indicating Behavior should listen for Mouse Motion Events
     */
    public final static int MOUSE_MOTION_LISTENER = 0x02;

    /**
     * Flag indicating Behavior should listen for Key Events
     */
    public final static int KEY_LISTENER = 0x04;

    public  TransformGroup  targetTG;

    private ArrayList eventQueue;

    private int listenerFlags;
    private Canvas3D canvas;

    /**
     * Constructs a new ViewPlatformAWTBehavior
     * @param c The Canvas3D on which to listen for events. If this is null a
     * NullPointerException will be thrown.
     * @param listenerFlags Indicates which listener should be registered,
     * one or more of MOUSE_LISTENER, MOUSE_MOTION_LISTENER, KEY_LISTENER
     */
    public ViewAWTBehavior(Canvas3D c, int listenerFlags ) {
        super();
        this.listenerFlags = listenerFlags;
        this.canvas = c;

        if (c==null)
            throw new NullPointerException();
    }

    /**
     * Initializes the behavior.
     * NOTE: Applications should not call this method. It is called by the
     * Java 3D behavior scheduler.
     */
    public void initialize() {
        eventQueue = new ArrayList();

        frameWakeup = new WakeupOnElapsedFrames( 0 );
        postWakeup = new WakeupOnBehaviorPost( this, POST_ID );

        // Sets the Viewpoint
        integrateTransforms();

        wakeupOn(postWakeup);
    }

    /**
     * Process a stimulus meant for this behavior.
     * NOTE: Applications should not call this method. It is called by the
     * Java 3D behavior scheduler.
     */
    public void processStimulus( java.util.Enumeration enum ) {
        while(enum.hasMoreElements()) {
            WakeupCondition wakeup = (WakeupCondition)enum.nextElement();
            if (wakeup instanceof WakeupOnBehaviorPost) {
                motion = true;
            } else if (wakeup instanceof WakeupOnElapsedFrames) {
                AWTEvent[] events = null;
                // access to LinkedList must be synchronized
                synchronized(eventQueue) {
                    events = (AWTEvent[])eventQueue.toArray( new AWTEvent[eventQueue.size()] );
                    eventQueue.clear();
                }
                processAWTEvents(events);

                if (motion)
                    integrateTransforms();
            }
        }

        if (motion) {
            // wake up on behavior posts and elapsed frames if in motion
            wakeupOn( frameWakeup );
        } else {
            // only wake up on behavior posts if not in motion
            wakeupOn( postWakeup );
        }
    }

    /**
     * Overload setEnable from Behavior.
     *
     * Adds/Removes the AWT listeners depending on the requested
     * state.
     */
    public void setEnable( boolean state ) {
        if (state==getEnable())
            return;

        super.setEnable(state);
        enableListeners( state );
    }

    private void enableListeners( boolean enable ) {
        if (enable) {
            if ( (listenerFlags & MOUSE_LISTENER)!=0)
                canvas.addMouseListener(this);

            if ( (listenerFlags & MOUSE_MOTION_LISTENER)!=0)
                canvas.addMouseMotionListener(this);

            if ( (listenerFlags & KEY_LISTENER)!=0)
              canvas.addKeyListener(this);
        } else {
            if ( (listenerFlags & MOUSE_LISTENER)!=0)
                canvas.removeMouseListener(this);

            if ( (listenerFlags & MOUSE_MOTION_LISTENER)!=0)
                canvas.removeMouseMotionListener(this);

            if ( (listenerFlags & KEY_LISTENER)!=0)
                canvas.removeKeyListener(this);
        }
    }

    /**
     * Sets the TransformGroup of the ViewPlatform for this Behavior.
     */
    public void setViewTransformGroup(TransformGroup tg) {
        targetTG   =   tg;
        if (targetTG == null)
            enableListeners( false );
        else
            enableListeners( true );
    }

    /**
     * Returns the TransformGroup of the ViewPlatform.
     */
     public TransformGroup getViewTransformGroup() {
        return targetTG;
     }

    /**
     * This is called once per frame if there are any AWT events to
     * process.
     *
     * The <code>motion</code> variable will be true when the method
     * is called. If it is true when the method returns integrateTransforms
     * will be called immediately.
     *
     * The AWTEvents are presented in the array in the order in which they
     * arrived from AWT.
     */
    protected abstract void processAWTEvents( final java.awt.AWTEvent[] events );

    /**
     * Called once per frame (if the view is moving) to calculate the new
     * view platform transform
     */
    protected abstract void integrateTransforms();

    /**
     * Queue AWTEvents in a thread safe manner.
     *
     * If subclasses override this method they must call
     * super.queueAWTEvent(e)
     */
    protected void queueAWTEvent( AWTEvent e ) {
        // add new event to the queue
        // must be MT safe
        synchronized (eventQueue) {
            eventQueue.add(e);
            // only need to post if this is the only event in the queue
            if (eventQueue.size() == 1) postId( POST_ID );
        }
    }

    public void mouseClicked(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void mouseEntered(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void mouseExited(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void mousePressed(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void mouseReleased(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void mouseDragged(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void mouseMoved(final MouseEvent e) {
        queueAWTEvent( e );
    }

    public void keyReleased(final java.awt.event.KeyEvent e) {
        queueAWTEvent( e );
    }

    public void keyPressed(final java.awt.event.KeyEvent e) {
        queueAWTEvent( e );
    }

    public void keyTyped(final java.awt.event.KeyEvent e) {
        queueAWTEvent( e );
    }
}

