/*
 *      @(#)KeyNavigatorBehavior.java 1.11 02/04/01 15:01:14
 *
 * Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 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.
 *
 * You acknowledge that Software is not designed,licensed or intended
 * for use in the design, construction, operation or maintenance of
 * any nuclear facility.
 */

package pibm.patou3d;

import java.awt.event.*;
import java.awt.AWTEvent;
import java.util.Enumeration;
import java.awt.Component;
import java.util.LinkedList;
import javax.vecmath.*;
import javax.media.j3d.*;
import com.sun.j3d.internal.J3dUtilsI18N;

/**
 * This class is a simple behavior that invokes the KeyNavigator
 * to modify the view platform transform.
 */
public class KeyNavigatorBehavior extends Behavior implements KeyListener {
    private WakeupCriterion w1 = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
    private WakeupCriterion w2 = new WakeupOnAWTEvent(KeyEvent.KEY_RELEASED);
    private WakeupOnElapsedFrames w3 = new WakeupOnElapsedFrames(0);
    private WakeupCriterion[] warray0 = { w1, w2};
    private WakeupCriterion[] warray1 = { w1, w2, w3 };
    private WakeupCondition[] w = {new WakeupOr(warray0), new WakeupOr(warray1)};
    
    private KeyEvent eventKey;
    private KeyNavigator keyNavigator;
    private boolean listener = false;

    private LinkedList eventq;


    /**
     *  Override Behavior's initialize method to setup wakeup criteria.
     */
    public void initialize() {
        // Establish initial wakeup criteria
        if (listener) {
            w1 = new WakeupOnBehaviorPost(this, KeyEvent.KEY_PRESSED);
            w2 = new WakeupOnBehaviorPost(this, KeyEvent.KEY_RELEASED);
            warray0[0] = w1;
            warray0[1] = w2;

            warray1[0] = w1;
            warray1[1] = w2;
            
            w[0] = new WakeupOr(warray0);
            w[1] = new WakeupOr(warray1);
            
            eventq = new LinkedList();
        }
        wakeupOn(w[0]);
    }

    /**
     *  Override Behavior's stimulus method to handle the event.
     */
    public void processStimulus(Enumeration criteria) {
        WakeupOnAWTEvent ev;
        WakeupCriterion genericEvt;
        AWTEvent[] events;
        boolean sawFrame = false;
   
        int lValeur = 1;
        
        while (criteria.hasMoreElements()) {
            genericEvt = (WakeupCriterion) criteria.nextElement();
            if (genericEvt instanceof WakeupOnAWTEvent) {
                ev = (WakeupOnAWTEvent) genericEvt;
                events = ev.getAWTEvent();
                lValeur = processAWTEvent(events);
            } else if (genericEvt instanceof WakeupOnElapsedFrames &&
                       eventKey != null) {
                sawFrame = true;
            } else if ((genericEvt instanceof WakeupOnBehaviorPost)) {
                while(true) {
                    // access to the queue must be synchronized
                    synchronized (eventq) {
                        if (eventq.isEmpty()) break;
                        eventKey  = (KeyEvent)eventq.remove(0);
                        if (eventKey.getID() == KeyEvent.KEY_PRESSED ||
                            eventKey.getID() == KeyEvent.KEY_RELEASED) {
                            lValeur = keyNavigator.processKeyEvent(eventKey);
                        }
                    }
                }
            }
        }
        if (sawFrame)
            keyNavigator.integrateTransformChanges();

        // Set wakeup criteria for next time
        if (lValeur == 0) {
            wakeupOn(w[0]);
        } else {
            wakeupOn(w[1]);
        }
    }

    /**
     *  Process a keyboard event
     */
    private int processAWTEvent(AWTEvent[] events) {
        int lValeur = 0;
        for (int loop = 0; loop < events.length; loop++) {
            if (events[loop] instanceof KeyEvent) {
                eventKey = (KeyEvent) events[loop];
                //  change the transformation; for example to zoom
                if (eventKey.getID() == KeyEvent.KEY_PRESSED ||
                    eventKey.getID() == KeyEvent.KEY_RELEASED) {
                    //System.out.println("Keyboard is hit! " + eventKey);
                    lValeur = keyNavigator.processKeyEvent(eventKey);
                }
            }
        }
        
        return lValeur;
    }

    /**
     * Adds this behavior as a KeyListener to the specified component.
     * This method can only be called if
     * the behavior was created with one of the constructors that takes
     * a Component as a parameter.
     * @param c The component to add the KeyListener to.
     * @exception IllegalStateException if the behavior was not created
     * as a listener
     * @since Java 3D 1.2.1
     */
    public void addListener(Component c) {
        if (!listener) {
            throw new IllegalStateException(J3dUtilsI18N.getString("Behavior0"));
        }
        c.addKeyListener(this);
    }

    /**
     * Constructs a new key navigator behavior node that operates
     * on the specified transform group.
     * @param targetTG the target transform group
     */
    public KeyNavigatorBehavior(TransformGroup targetTG) {
        keyNavigator = new KeyNavigator(targetTG);
    }

    /**
     * Constructs a key navigator behavior that uses AWT listeners
     * and behavior posts rather than WakeupOnAWTEvent.  The behavior
     * is added to the specified Component and works on the given
     * TransformGroup.  A null component can be passed to specify
     * the behavior should use listeners.  Components can then be added
     * to the behavior with the addListener(Component c) method.
     * @param c The component to add the KeyListener to.
     * @param targetTG The target transform group.
     * @since Java 3D 1.2.1
     */
    public KeyNavigatorBehavior(Component c, TransformGroup targetTG) {
        this(targetTG);
        if (c != null) {
            c.addKeyListener(this);
        }
        listener = true;      
    }

    public void keyPressed(KeyEvent evt) {
//      System.out.println("keyPressed");

        // add new event to the queue
        // must be MT safe
        synchronized (eventq) {
            eventq.add(evt);
            // only need to post if this is the only event in the queue
            if (eventq.size() == 1) postId(KeyEvent.KEY_PRESSED);
        }
    }

    public void keyReleased(KeyEvent evt) {
//      System.out.println("keyReleased");

        // add new event to the queue
        // must be MT safe
        synchronized (eventq) {
            eventq.add(evt);
            // only need to post if this is the only event in the queue
            if (eventq.size() == 1) postId(KeyEvent.KEY_RELEASED);
        }
    }

    public void keyTyped(KeyEvent evt) {}

}
