/*
 * $RCSfile: ViewCache.java,v $
 *
 * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 * $Revision: 1.7 $
 * $Date: 2008/02/28 20:17:32 $
 * $State: Exp $
 */

package javax.media.j3d;

import javax.vecmath.*;

/**
 * The ViewCache class is used to cache all data, both API data and derived
 * data, that is independent of the Canvas3D and Screen3D.
 */
class ViewCache extends Object {
    // The view associated with this view cache
    View view;

    //
    // API/INPUT DATA
    //

    // *********************
    // * From ViewPlatform *
    // *********************
    int		viewAttachPolicy;

    // *********************
    // * From PhysicalBody *
    // *********************

    /**
     * The user's left eye's position in head coordinates.
     */
    Point3d	leftEyePosInHead = new Point3d();

    /**
     * The user's right eye's position in head coordinates.
     */
    Point3d	rightEyePosInHead = new Point3d();

    /**
     * The user's left ear's position in head coordinates.
     */
    Point3d	leftEarPosInHead = new Point3d();

    /**
     * The user's right ear's position in head coordinates.
     */
    Point3d	rightEarPosInHead = new Point3d();

    /**
     * The user's nominal eye height as measured
     * from the ground plane.
     */
    double	nominalEyeHeightFromGround;

    /**
     * The amount to offset the system's
     * viewpoint from the user's current eye-point.  This offset
     * distance allows an "Over the shoulder" view of the scene
     * as seen by the user.
     */
    double	nominalEyeOffsetFromNominalScreen;

    // Head to head-tracker coordinate system transform.
    // If head tracking is enabled, this transform is a calibration
    // constant.  If head tracking is not enabled, this transform is
    // not used.
    // This is only used in SCREEN_VIEW mode.
    Transform3D headToHeadTracker = new Transform3D();

    // ****************************
    // * From PhysicalEnvironment *
    // ****************************

    // Coexistence coordinate system to tracker-base coordinate
    // system transform.  If head tracking is enabled, this transform
    // is a calibration constant.  If head tracking is not enabled,
    // this transform is not used.
    // This is used in both SCREEN_VIEW and HMD_VIEW modes.
    Transform3D coexistenceToTrackerBase = new Transform3D();

    // Transform generated by the head tracker to transform
    // from tracker base to head tracker coordinates (and the inverse).
    Transform3D headTrackerToTrackerBase = new Transform3D();
    Transform3D trackerBaseToHeadTracker = new Transform3D();

    //
    // Indicates whether the underlying hardware implementation
    // supports tracking.
    //
    boolean trackingAvailable;

    // Sensor index for head tracker
    int headIndex;

    //
    // This variable specifies the policy Java 3D will use in placing
    // the user's eye position relative to the user's head position
    // (NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET).
    // It is used in the calibration process.
    //
    int coexistenceCenterInPworldPolicy;


    // *************
    // * From View *
    // *************

    // View model compatibility mode flag
    boolean compatibilityModeEnable;

    // coexistenceCenteringEnable flag
    boolean coexistenceCenteringEnable;

    Point3d leftManualEyeInCoexistence = new Point3d();
    Point3d rightManualEyeInCoexistence = new Point3d();

    // Indicates which major mode of view computation to use:
    // HMD mode or screen/fish-tank-VR mode.
    int		viewPolicy;

    // The current projection policy (parallel versus perspective)
    int projectionPolicy;

    // The current screen scale policy and scale value
    int screenScalePolicy;
    double screenScale;

    // The current window resize, movement and eyepoint policies
    int windowResizePolicy;
    int windowMovementPolicy;
    int windowEyepointPolicy;

    // The current monoscopic view policy
    int monoscopicViewPolicy;

    // The view model's field of view.
    double	fieldOfView;

    // The distance away from the clip origin
    // in the direction of gaze for the front and back clip planes.
    double	frontClipDistance;
    double	backClipDistance;

    // Front and back clip policies
    int		frontClipPolicy;
    int		backClipPolicy;

    // ViewPlatform of this view
    ViewPlatformRetained vpRetained;

    /**
     * Defines the visibility policy.
     */
    int		visibilityPolicy;

    // Flag to enable tracking, if so allowed by the trackingAvailable flag.
    boolean		trackingEnable;

    // This setting enables the continuous updating by Java 3D of the
    // userHeadToVworld transform.
    boolean userHeadToVworldEnable;

    // The current compatibility mode view transform
    Transform3D compatVpcToEc = new Transform3D();

    // The current compatibility mode projection transforms
    Transform3D compatLeftProjection = new Transform3D();
    Transform3D compatRightProjection = new Transform3D();

    // Mask that indicates ViewCache's view dependence info. has changed,
    // and CanvasViewCache may need to recompute the final view matries. 
    int vcDirtyMask = 0;

    //
    // DERIVED DATA
    //

    // Flag indicating that head tracking will be used
    private boolean doHeadTracking;

    //
    // Matrix to transform from user-head to
    // virtual-world coordinates. This matrix is a read-only
    // value that Java 3D generates continuously, but only if enabled
    // by userHeadToVworldEnableFlag.
    //
    Transform3D	userHeadToVworld = new Transform3D();


    /**
     * Take snapshot of all per-view API parameters and input values.
     */
    synchronized void snapshot() {
	
	// View parameters
	vcDirtyMask = view.vDirtyMask;
	view.vDirtyMask = 0;
	compatibilityModeEnable = view.compatibilityModeEnable;
	coexistenceCenteringEnable = view.coexistenceCenteringEnable;
	leftManualEyeInCoexistence.set(view.leftManualEyeInCoexistence);
	rightManualEyeInCoexistence.set(view.rightManualEyeInCoexistence);
	viewPolicy = view.viewPolicy;
	projectionPolicy = view.projectionPolicy;
	screenScalePolicy = view.screenScalePolicy;
	windowResizePolicy = view.windowResizePolicy;
	windowMovementPolicy = view.windowMovementPolicy;	
	windowEyepointPolicy = view.windowEyepointPolicy;
	monoscopicViewPolicy = view.monoscopicViewPolicy;

	fieldOfView = view.fieldOfView;
	screenScale = view.screenScale;
	
	frontClipDistance = view.frontClipDistance;
	backClipDistance = view.backClipDistance;	
	frontClipPolicy = view.frontClipPolicy;
	backClipPolicy = view.backClipPolicy;

	visibilityPolicy = view.visibilityPolicy;
	
	trackingEnable = view.trackingEnable;
	userHeadToVworldEnable = view.userHeadToVworldEnable;

	view.compatVpcToEc.getWithLock(compatVpcToEc);
	view.compatLeftProjection.getWithLock(compatLeftProjection);
	view.compatRightProjection.getWithLock(compatRightProjection);

	// ViewPlatform parameters
	ViewPlatform vpp = view.getViewPlatform();

	if (vpp == null) {
	    // This happens when user attach a null viewplatform
	    // and MC still call updateViewCache() in run before
	    // the viewDeactivate request get.
	    return;
	}

	vpRetained = (ViewPlatformRetained) vpp.retained;
	
	synchronized(vpRetained) {
	    vcDirtyMask |= vpRetained.vprDirtyMask; 
	    vpRetained.vprDirtyMask = 0;
	    viewAttachPolicy = vpRetained.viewAttachPolicy;
	    // System.err.println("ViewCache snapshot vcDirtyMask " + vcDirtyMask );
	}
	
	// PhysicalEnvironment parameters
	PhysicalEnvironment env = view.getPhysicalEnvironment();

	synchronized(env) {
	    vcDirtyMask |= env.peDirtyMask; 
	    env.peDirtyMask = 0;
	    
	    env.coexistenceToTrackerBase.getWithLock(coexistenceToTrackerBase);
	    trackingAvailable = env.trackingAvailable;
	    coexistenceCenterInPworldPolicy = env.coexistenceCenterInPworldPolicy;
	    
	    // NOTE: this is really derived data, but we need it here in order
	    // to avoid reading head tracked data when no tracker is available
	    // and enabled.
	    doHeadTracking = trackingEnable && trackingAvailable;
	    
	    if (doHeadTracking) {
		headIndex = env.getHeadIndex();
		env.getSensor(headIndex).getRead(headTrackerToTrackerBase);
		vcDirtyMask |= View.TRACKING_ENABLE_DIRTY;
	    }
	    else {
		headTrackerToTrackerBase.setIdentity();
	    }
	}
	
	// PhysicalBody parameters
	PhysicalBody body = view.getPhysicalBody();

	synchronized(body) {
	    vcDirtyMask |= body.pbDirtyMask; 
	    body.pbDirtyMask = 0;
	    
	    leftEyePosInHead.set(body.leftEyePosition);
	    rightEyePosInHead.set(body.rightEyePosition);
	    leftEarPosInHead.set(body.leftEarPosition);
	    rightEarPosInHead.set(body.rightEarPosition);

	    nominalEyeHeightFromGround = body.nominalEyeHeightFromGround;
	    nominalEyeOffsetFromNominalScreen =
		body.nominalEyeOffsetFromNominalScreen;
	}

	body.headToHeadTracker.getWithLock(headToHeadTracker);
    }


    /**
     * Compute derived data using the snapshot of the per-view data.
     */
    synchronized void computeDerivedData() {
	if (doHeadTracking) {
	    trackerBaseToHeadTracker.invert(headTrackerToTrackerBase);
	    //System.err.println("trackerBaseToHeadTracker: ");
	    //System.err.println(trackerBaseToHeadTracker);
	}
	else {
	    trackerBaseToHeadTracker.setIdentity();
	}

	// XXXX: implement head to vworld tracking if userHeadToVworldEnable is set
    	userHeadToVworld.setIdentity();
    }


    // Get methods for returning derived data values.
    // Eventually, these get functions will cause some of the parameters
    // to be lazily evaluated.
    //
    // NOTE that in the case of Transform3D, and Tuple objects, a reference
    // to the actual derived data is returned.  In these cases, the caller
    // must ensure that the returned data is not modified.

    boolean getDoHeadTracking() {
	return doHeadTracking;
    }

    /**
     * Constructs and initializes a ViewCache object.
     */
    ViewCache(View view) {
	this.view = view;

	if (false)
	    System.err.println("Constructed a ViewCache");
    }

}
