/*
 * $RCSfile: TransparencyInterpolator.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision: 1.4 $
 * $Date: 2007/02/09 17:18:28 $
 * $State: Exp $
 */

package javax.media.j3d;

import java.util.Enumeration;

/**
 * TransparencyInterpolator behavior.  This class defines a behavior
 * that modifies the transparency of its target TransparencyAttributes
 * object by linearly interpolating between a pair of specified 
 * transparency values (using the value generated by the specified
 * Alpha object). 
 * <P>
 * There are two forms of constructor to specify the
 * type of transparency interpolation. The first constructor takes 
 * an Alpha and a TransparencyAttributes  object and creates a transparency
 * interpolator that maps an Alpha value of 1.0 to a transparency
 * value of 1.0, and an Alpha value of 0.0 and maps it to a
 * transparency value of 0.0. The second constructor takes an Alpha,
 * a TransparencyAttributes object, a minimum transparency value and a
 * maximum transparency value. This constructor provides more
 * flexibility by specifying how the Alpha values are mapped
 * to the transparency values - an Alpha of 1.0 maps to the
 * maximum transparency value and an Alpha of 0.0 maps to the
 * minimum transparency value.<P>
 *
 * @see Alpha
 * @see TransparencyAttributes
 */


public class TransparencyInterpolator extends Interpolator {

    TransparencyAttributes target;
    float minimumTransparency;
    float maximumTransparency;

    // We can't use a boolean flag since it is possible 
    // that after alpha change, this procedure only run
    // once at alpha.finish(). So the best way is to
    // detect alpha value change.
    private float prevAlphaValue = Float.NaN;
    private WakeupCriterion passiveWakeupCriterion = 
	(WakeupCriterion) new WakeupOnElapsedFrames(0, true);

    // non-public, default constructor used by cloneNode
    TransparencyInterpolator() {
    }

    /**
      * Constructs a trivial transparency interpolator with a specified target,
      * a minimum transparency of 0.0f and a maximum transparency of 1.0f.
      * @param alpha the alpha object for this interpolator
      * @param target the TransparencyAttributes component object affected 
      * by this interpolator
      */
    public TransparencyInterpolator(Alpha alpha,
				    TransparencyAttributes target) {

	super(alpha);

	this.target = target;
	this.minimumTransparency = 0.0f;
	this.maximumTransparency = 1.0f;
    }

    /**
      * Constructs a new transparency interpolator that varies the target
      * material's transparency between the two transparency values.
      * @param alpha the alpha object for this Interpolator
      * @param target the TransparencyAttributes component object affected 
      * by this interpolator
      * @param minimumTransparency the starting transparency
      * @param maximumTransparency the ending transparency
     */
    public TransparencyInterpolator(Alpha alpha,
				    TransparencyAttributes target,
				    float minimumTransparency,
				    float maximumTransparency) {

	super(alpha);

	this.target = target;
	this.minimumTransparency = minimumTransparency;
	this.maximumTransparency = maximumTransparency;
    }
    
    /**
      * This method sets the minimumTransparency for this interpolator.
      * @param transparency the new minimum transparency
      */
    public void setMinimumTransparency(float transparency) {
	this.minimumTransparency = transparency;
    }

    /**
      * This method retrieves this interpolator's minimumTransparency.
      * @return the interpolator's minimum transparency value
      */
    public float getMinimumTransparency() {
	return this.minimumTransparency;
    }

    /**
      * This method sets the maximumTransparency for this interpolator.
      * @param transparency the new maximum transparency
      */
    public void setMaximumTransparency(float transparency) {
	this.maximumTransparency = transparency;
    }

    /**
      * This method retrieves this interpolator's maximumTransparency.
      * @return the interpolator's maximal transparency vslue
      */
    public float getMaximumTransparency() {
	return this.maximumTransparency;
    }

    /**
      * This method sets the target TransparencyAttributes object
      * for this interpolator.
      * @param target the target TransparencyAttributes object
      */
    public void setTarget(TransparencyAttributes target) {
	this.target = target;
    }

    /**
      * This method retrieves this interpolator's target reference.
      * @return the interpolator's target TransparencyAttributes object
      */
    public TransparencyAttributes getTarget() {
	return target;
    }

    // The TransparencyInterpolator's initialize routine uses the default 
    // initialization routine.

    /**
     * This method is invoked by the behavior scheduler every frame.  It
     * maps the alpha value that corresponds to the current time into a
     * transparency value and updates the specified TransparencyAttributes
     * object with this new transparency value.
     * @param criteria an enumeration of the criteria that caused the
     * stimulus
     */
    public void processStimulus(Enumeration criteria) {
	// Handle stimulus
	WakeupCriterion criterion = passiveWakeupCriterion;

	if (alpha != null) {
	    float value = alpha.value();

	    if (value != prevAlphaValue) {
		float val = (float)((1.0-value)*minimumTransparency +
				    value*maximumTransparency);

		target.setTransparency(val);
		prevAlphaValue = value;
	    }
	    if (!alpha.finished() && !alpha.isPaused()) {
		criterion = defaultWakeupCriterion;
	    }
	}
	wakeupOn(criterion);
    }

    /**
     * Used to create a new instance of the node.  This routine is called
     * by <code>cloneTree</code> to duplicate the current node.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @see Node#cloneTree
     * @see Node#cloneNode
     * @see Node#duplicateNode
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public Node cloneNode(boolean forceDuplicate) {
        TransparencyInterpolator ti = new TransparencyInterpolator();
        ti.duplicateNode(this, forceDuplicate);
        return ti;
    }


   /**
     * Copies all TransparencyInterpolator information from
     * <code>originalNode</code> into
     * the current node.  This method is called from the
     * <code>cloneNode</code> method which is, in turn, called by the
     * <code>cloneTree</code> method.<P>
     *
     * @param originalNode the original node to duplicate.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @exception RestrictedAccessException if this object is part of a live
     *  or compiled scenegraph.
     *
     * @see Node#duplicateNode
     * @see Node#cloneTree
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
        super.duplicateAttributes(originalNode, forceDuplicate);

	TransparencyInterpolator ti  =
	    (TransparencyInterpolator) originalNode;

        setMinimumTransparency(ti.getMinimumTransparency());
        setMaximumTransparency(ti.getMaximumTransparency());

	// this reference will be updated in updateNodeReferences()
        setTarget(ti.getTarget());
    }

    /**
     * Callback used to allow a node to check if any nodes referenced
     * by that node have been duplicated via a call to <code>cloneTree</code>.
     * This method is called by <code>cloneTree</code> after all nodes in
     * the sub-graph have been duplicated. The cloned Leaf node's method
     * will be called and the Leaf node can then look up any node references
     * by using the <code>getNewObjectReference</code> method found in the
     * <code>NodeReferenceTable</code> object.  If a match is found, a
     * reference to the corresponding Node in the newly cloned sub-graph
     * is returned.  If no corresponding reference is found, either a
     * DanglingReferenceException is thrown or a reference to the original
     * node is returned depending on the value of the
     * <code>allowDanglingReferences</code> parameter passed in the
     * <code>cloneTree</code> call.
     * <p>
     * NOTE: Applications should <i>not</i> call this method directly.
     * It should only be called by the cloneTree method.
     *
     * @param referenceTable a NodeReferenceTableObject that contains the
     *  <code>getNewObjectReference</code> method needed to search for
     *  new object instances.
     * @see NodeReferenceTable
     * @see Node#cloneTree
     * @see DanglingReferenceException
     */
    public void updateNodeReferences(NodeReferenceTable referenceTable) {
        super.updateNodeReferences(referenceTable);

        // check TransparencyAttributes
        NodeComponent nc = getTarget();

        if (nc != null) {
            setTarget((TransparencyAttributes)  referenceTable.getNewObjectReference(nc));
        }
    }


}
