/*
 * @(#)TestCase.java
 *
 * Copyright (c) 2001-02, Pharmix Corp.  All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of 
 * Pharmix Corp. ("Confidential Information").  You shall not
 * disclose such Confidential Information.  This software is provided for
 * evaluation purposes only.  Commercial use is prohibited.
 *   
 */

import java.awt.*;
import javax.swing.*;

import javax.media.j3d.*;
import javax.vecmath.*;

import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.universe.*;


/**
 * A simple class for Java3D test cases.
 *
 * @author  Sean Sylvis
 */
public class TestCase extends JFrame
{
    // Constants
    public static final int kDefaultWidth = 800;
    public static final int kDefaultHeight = 600;

    // Members
    protected SimpleUniverse mUniverse;
    protected BranchGroup mGraphBranch;
    protected TransformGroup mSceneTransform;
    protected TransformGroup mViewTransform;

    // TestCase members
    protected double[] mCoordinates;
    protected float[] mNormals;


    /**
     * Constructs a default TestCase.
     */
    public TestCase()
    {
        this(kDefaultWidth, kDefaultHeight);
    }


    /**
     * Constructs a TestCase with the parameter dimensions.
     *
     * @param pWidth  the drawing canvas width
     * @param pHeight  the drawing canvas height
     */
    public TestCase(int pWidth, int pHeight)
    {
        try
        {
            // Initialize frame properties
            super.setTitle("Java3D TestCase Viewer");
            
            super.setSize(pWidth, pHeight);
            super.setDefaultCloseOperation(super.EXIT_ON_CLOSE);
            
            // Add a canvas
            Canvas3D vCanvas = new Canvas3D(createGraphicsConfig());
            super.getContentPane().add(vCanvas, BorderLayout.CENTER);

            // Setup universe
            mUniverse = new SimpleUniverse(vCanvas);
            setupSceneBranch();
            setupViewBranch();
            
            // Adjust the view z-coordinate
            Vector3d vView = new Vector3d(0, 0, 20);

            // Move the viewpoint in the universe
            Transform3D vTransform = new Transform3D();
            vTransform.setTranslation(vView);
            mViewTransform.setTransform(vTransform);
            
            super.setVisible(true);
        }
        
        catch (Exception e)
        {
            System.out.println("Could not create TestCase.");
        }
    }


    /**
     * Runs the test case.
     */
    public void runTest()
    {
        // Set #sphere divisions
        int kNumDivisions = 8;

        // Compute the sphere geometry
        TriangleStripArray vTriangleStrips = computeGeometry(kNumDivisions);

        // Create appearance
        Appearance vAppearance = new Appearance();

        // Set color
        Material vMaterial = new Material();
        vMaterial.setDiffuseColor(new Color3f(Color.red));
        vAppearance.setMaterial(vMaterial);

        PolygonAttributes vPolygon = new PolygonAttributes();
        vPolygon.setPolygonMode(PolygonAttributes.POLYGON_FILL);
        vAppearance.setPolygonAttributes(vPolygon);
        
        BranchGroup vObject = new BranchGroup();
        vObject.addChild(new Shape3D(vTriangleStrips, vAppearance));
        
        mGraphBranch.addChild(vObject);
    }
    

    /**
     * Computes the geometry of this display object and stores the information
     * (coordinates, normals, etc.) in its geometry array.
     */
    protected TriangleStripArray computeGeometry(int pDivisions)
    {
        // Use by-reference geometry
        boolean kUseReference = true;

        // Set #coordinates
        int vNumCoordinates = (pDivisions+1) * (pDivisions+1) * 2;

        int[] vTempStripCounts = { vNumCoordinates };
        
        // Set flags
        int vFlags = GeometryArray.COORDINATES | GeometryArray.NORMALS;
        if (kUseReference)
            vFlags |= GeometryArray.BY_REFERENCE;
        
        // Create geometry array
        TriangleStripArray vTriangleStrips = new TriangleStripArray(vNumCoordinates,
                                                                    vFlags,
                                                                    vTempStripCounts);
        
        mCoordinates = new double[vNumCoordinates*3];
        mNormals = new float[vNumCoordinates*3];
        
    
        // Compute the delta radians
        double vAxisDeltaRadians = Math.PI / pDivisions;
        double vCircleDeltaRadians = (2.0 * Math.PI) / pDivisions;

        // Set number of triangle strips
        int[] vStripCounts = new int[pDivisions];

        int vStripIndex = 0;
        int vCoordIndex = 0;

        // Iterate along sphere axis
        for (int i = 0; i < pDivisions; i++)
        {
            // Start new triangle strip
            int vStripVertexCount = 0;
            
            // Iterate around circle
            for (int j = 0; j <= pDivisions; j++)
            {
                // Set coordinate
                setCoordinate(vCoordIndex, i, j, vAxisDeltaRadians, vCircleDeltaRadians);
                vCoordIndex += 3;
                vStripVertexCount++;

                // Set next strip coordinate
                setCoordinate(vCoordIndex, i+1, j, vAxisDeltaRadians, vCircleDeltaRadians);
                vCoordIndex += 3;
                vStripVertexCount++;
            }

            // Set triangle strip length
            vStripCounts[vStripIndex++] = vStripVertexCount;
        }

        // Set the triangle strip vertex counts
        vTriangleStrips.setStripVertexCounts(vStripCounts);

        // Set arrays
        if (kUseReference)
        {
            vTriangleStrips.setCoordRefDouble(mCoordinates);
            vTriangleStrips.setNormalRefFloat(mNormals);
        }
        else
        {
            vTriangleStrips.setCoordinates(0, mCoordinates);
            vTriangleStrips.setNormals(0, mNormals);
        }

        return vTriangleStrips;
    }


    /**
     * Sets the sphere coordinate for the parameter (i,j) and delta values.
     *
     * @param i  the axis index
     * @param j  the circle index
     * @param pAxisDeltaRadians  the axis delta
     * @param pCircleDeltaRadians  the circle delta
     **/
    protected void setCoordinate(int pCoordIndex,
                                 int i,
                                 int j,
                                 double pAxisDeltaRadians,
                                 double pCircleDeltaRadians)
    {
        double vCircleRadius = Math.sin(pAxisDeltaRadians * i);
        
        // Compute unit coordinates
        double vX = Math.sin(pCircleDeltaRadians * j) * vCircleRadius;
        double vY = Math.cos(pAxisDeltaRadians * i);
        double vZ = Math.cos(pCircleDeltaRadians * j) * vCircleRadius;

        // Set coordinate
        mCoordinates[pCoordIndex] = vX;
        mCoordinates[pCoordIndex+1] = vY;
        mCoordinates[pCoordIndex+2] = vZ;
        
        // Set normal
        mNormals[pCoordIndex] = (float)vX;
        mNormals[pCoordIndex+1] = (float)vY;
        mNormals[pCoordIndex+2] = (float)vZ;
    }


    /**
     * Sets up the scene branch part of the Java3D scene graph.
     *
     * First, creates the root of the branch, then adds a transform and mouse 
     * behaviors and the root of the scene objects.
     */
    protected void setupSceneBranch()
    {
        // Create root of scene branch
        BranchGroup vSceneBranch = new BranchGroup();
        vSceneBranch.setCapability(BranchGroup.ALLOW_DETACH);

        // Create transform group of mouse behavior
        TransformGroup vMouseTransform = new TransformGroup();
        vMouseTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        vMouseTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        // Create transform group of scene branch
        mSceneTransform = new TransformGroup();
        mSceneTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        mSceneTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        vMouseTransform.addChild(mSceneTransform);
        vSceneBranch.addChild(vMouseTransform);

        // Behaviors
        BoundingSphere vBounds = new BoundingSphere(new Point3d(0,0,0),
                                                    60.0f);
        
        MouseRotate vRotate = new MouseRotate(vMouseTransform);
        vRotate.setSchedulingBounds(vBounds);

        MouseTranslate vTranslate = new MouseTranslate(vMouseTransform);
        vTranslate.setSchedulingBounds(vBounds);

        MouseZoom vZoom = new MouseZoom(vMouseTransform);
        vZoom.setSchedulingBounds(vBounds);

        // Link behaviors to view branch
        vMouseTransform.addChild(vRotate);
        vMouseTransform.addChild(vTranslate);
        vMouseTransform.addChild(vZoom);

        // Setup base of graph branch
        mGraphBranch = new BranchGroup();
        mGraphBranch.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        mGraphBranch.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        mGraphBranch.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

        // Attach to scene branch
        mSceneTransform.addChild(mGraphBranch);

        vSceneBranch.compile();
        mUniverse.addBranchGraph(vSceneBranch);
    }


    /**
     * Sets up the view branch part of the Java3D scene graph.
     *
     * Sets the clipping distances, window resize policy, and adds the
     * lighting properties.
     */
    protected void setupViewBranch()
    {
        View vView = mUniverse.getViewer().getView();
        
        // Set the clipping distances
        vView.setFrontClipPolicy(View.VIRTUAL_EYE);
        vView.setBackClipPolicy(View.VIRTUAL_EYE);
        vView.setBackClipDistance(80.0);
        
        vView.setWindowResizePolicy(View.VIRTUAL_WORLD);
        //vView.setTransparencySortingPolicy(View.TRANSPARENCY_SORT_GEOMETRY);
        //vView.setSceneAntialiasingEnable(true);

        // Set view transform
        mViewTransform = mUniverse.getViewingPlatform().getMultiTransformGroup().getTransformGroup(0);

        // Create lighting branch
        BranchGroup vLighting = new BranchGroup();
        addLighting(vLighting);

        // Compile and attach to locale
        vLighting.compile();
        mUniverse.addBranchGraph(vLighting);
    }


    /**
     * Creates an ambient and directional light, sets up their influencing bounds,
     * and adds them to the view branch.
     *
     * @param pLighting  the root of lighting branch
     */
    protected void addLighting(BranchGroup pLighting)
    {
        // Set universe bounds of influence
        BoundingSphere vBounds = new BoundingSphere(new Point3d(0,0,0), 80.0f);
        
        // Create a lighting model
        AmbientLight vAmbient = new AmbientLight();
        DirectionalLight vDirectional = new DirectionalLight();

        // Setup influencing region
        vAmbient.setInfluencingBounds(vBounds);
        vDirectional.setInfluencingBounds(vBounds);

        // Add to scene graph
        pLighting.addChild(vAmbient);
        pLighting.addChild(vDirectional);
    }


    /**
     * Creates a graphics template that Canvas3D uses to create a canvas and
     * returns the configuration.
     */
    public static GraphicsConfiguration createGraphicsConfig()
    {
        // Create the template
        GraphicsConfigTemplate3D vGraphicsTemplate = new GraphicsConfigTemplate3D();
        
        vGraphicsTemplate.setStereo(GraphicsConfigTemplate3D.PREFERRED);
        vGraphicsTemplate.setSceneAntialiasing(GraphicsConfigTemplate3D.PREFERRED);

        // Then find a configuration that fits
        GraphicsDevice vDevice = 
            GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();

        return vDevice.getBestConfiguration(vGraphicsTemplate);
    }


    /**
     *
     */
    public static void main(String[] pArguments)
    {
        try
        {
            TestCase vTest = new TestCase();
            vTest.runTest();
        }

        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
}

