import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame; 
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.Event;
import java.awt.AWTEvent;
import java.awt.event.MouseEvent;
import com.sun.j3d.utils.picking.*;
import java.util.*;
import java.awt.*;
import javax.vecmath.*;
import java.text.*;


public class Small extends Applet {

    PickBehavior picker;
    Canvas3D canvas3D;


    Point3f[][] createCoordinateData() {
        //float[] data = new float[69*3];         // ******
        Point3f[][] data = new Point3f[9][];
        
        data[0] = new Point3f[17];
        data[1] = new Point3f[17];
        data[2] = new Point3f[5];
        data[3] = new Point3f[5];
        data[4] = new Point3f[5];
        data[5] = new Point3f[5];
        data[6] = new Point3f[5];
        data[7] = new Point3f[5];
        data[8] = new Point3f[5];

        int i = 0;
        data[0][i++]= new Point3f(-1.3f,-0.3f,0.3f); //0
        data[0][i++]= new Point3f(-0.9f,-0.3f,0.3f); //1
        data[0][i++]= new Point3f(-0.8f,-0.1f,0.3f); //2
        data[0][i++]= new Point3f(-0.6f,-0.1f,0.3f); //3
        data[0][i++]= new Point3f(-0.5f,-0.3f,0.3f); //4
        data[0][i++]= new Point3f(0.2f,-0.3f,0.3f); //5
        data[0][i++]= new Point3f(0.3f,-0.1f,0.3f); //6
        data[0][i++]= new Point3f(0.5f,-0.1f,0.3f); //7
        data[0][i++]= new Point3f(0.6f,-0.3f,0.3f); //8
        data[0][i++]= new Point3f(1.3f,-0.3f,0.3f); //9
        data[0][i++]= new Point3f(1.2f,-0.1f,0.3f); //10
        data[0][i++]= new Point3f(0.5f,0.0f,0.3f); //11
        data[0][i++]= new Point3f(0.1f,0.3f,0.3f); //12
        data[0][i++]= new Point3f(-0.5f,0.3f,0.3f); //13
        data[0][i++]= new Point3f(-1.1f,0.0f,0.3f); //14
        data[0][i++]= new Point3f(-1.3f,0.0f,0.3f); //15
        data[0][i++]= new Point3f(-1.3f,-0.3f,0.3f); //16
        System.out.println("end polygon; total vertex count: "+i);

        i = 0;
        data[1][i++]= new Point3f(-1.3f,-0.3f,-0.3f); // 0 17
        data[1][i++]= new Point3f(-1.3f, 0.0f,-0.3f); // 1 18
        data[1][i++]= new Point3f(-1.1f, 0.0f,-0.3f); // 2 19
        data[1][i++]= new Point3f(-0.5f, 0.3f,-0.3f); // 3 20
        data[1][i++]= new Point3f( 0.1f, 0.3f,-0.3f); // 4 21
        data[1][i++]= new Point3f( 0.5f, 0.0f,-0.3f); // 5 22
        data[1][i++]= new Point3f( 1.2f,-0.1f,-0.3f); // 6 23
        data[1][i++]= new Point3f( 1.3f,-0.3f,-0.3f); // 7 24
        data[1][i++]= new Point3f( 0.6f,-0.3f,-0.3f); // 8 25
        data[1][i++]= new Point3f( 0.5f,-0.1f,-0.3f); // 9 26
        data[1][i++]= new Point3f( 0.3f,-0.1f,-0.3f); //10 27
        data[1][i++]= new Point3f( 0.2f,-0.3f,-0.3f); //11 28
        data[1][i++]= new Point3f(-0.5f,-0.3f,-0.3f); //12 29
        data[1][i++]= new Point3f(-0.6f,-0.1f,-0.3f); //13 30
        data[1][i++]= new Point3f(-0.8f,-0.1f,-0.3f); //14 31
        data[1][i++]= new Point3f(-0.9f,-0.3f,-0.3f); //15 32
        data[1][i++]= new Point3f(-1.3f,-0.3f,-0.3f); //16 33
        System.out.println("end polygon; total vertex count: "+i);
        
        i = 0;
        data[2][i++]= new Point3f( 1.3f,-0.3f,-0.3f); // 0 34
        data[2][i++]= new Point3f( 1.2f,-0.1f,-0.3f); // 1 35
        data[2][i++]= new Point3f( 1.2f,-0.1f,0.3f); // 2 36
        data[2][i++]= new Point3f( 1.3f,-0.3f,0.3f); // 3 37
        data[2][i++]= new Point3f( 1.3f,-0.3f,-0.3f); // 4 38
        System.out.println("end polygon; total vertex count: "+i);

        i = 0;
        data[3][i++]= new Point3f( 1.2f,-0.1f,-0.3f); // 0 39
        data[3][i++]= new Point3f( 0.5f, 0.0f,-0.3f); // 1 40
        data[3][i++]= new Point3f( 0.5f, 0.0f,0.3f); // 2 41
        data[3][i++]= new Point3f( 1.2f,-0.1f,0.3f); // 3 42
        data[3][i++]= new Point3f( 1.2f,-0.1f,-0.3f); // 4 43
        System.out.println("end polygon; total vertex count: "+i);

        i = 0;
        data[4][i++]= new Point3f( 0.5f, 0.0f,-0.3f); // 0 44
        data[4][i++]= new Point3f( 0.1f, 0.3f,-0.3f); // 1 45
        data[4][i++]= new Point3f( 0.1f, 0.3f,0.3f); // 2 46
        data[4][i++]= new Point3f( 0.5f, 0.0f,0.3f); // 3 47
        data[4][i++]= new Point3f( 0.5f, 0.0f,-0.3f); // 4 48
        System.out.println("end polygon; total vertex count: "+i);

        i = 0;
        data[5][i++]= new Point3f( 0.1f, 0.3f,-0.3f); // 0 49
        data[5][i++]= new Point3f(-0.5f, 0.3f,-0.3f); // 1 50
        data[5][i++]= new Point3f(-0.5f, 0.3f,0.3f); // 2 51
        data[5][i++]= new Point3f( 0.1f, 0.3f,0.3f); // 3 52
        data[5][i++]= new Point3f( 0.1f, 0.3f,-0.3f); // 4 53
        System.out.println("end polygon; total vertex count: "+i);

        i = 0;
        data[6][i++]= new Point3f(-0.5f, 0.3f,-0.3f); // 0 54
        data[6][i++]= new Point3f(-1.1f, 0.0f,-0.3f); // 1 55
        data[6][i++]= new Point3f(-1.1f, 0.0f,0.3f); // 2 56
        data[6][i++]= new Point3f(-0.5f, 0.3f,0.3f); // 3 57
        data[6][i++]= new Point3f(-0.5f, 0.3f,-0.3f); // 4 58
        System.out.println("end polygon; total vertex count: "+i);

        i = 0;
        data[7][i++]= new Point3f(-1.1f, 0.0f,-0.3f); // 0 59
        data[7][i++]= new Point3f(-1.3f, 0.0f,-0.3f); // 1 60
        data[7][i++]= new Point3f(-1.3f, 0.0f,0.3f); // 2 61
        data[7][i++]= new Point3f(-1.1f, 0.0f,0.3f); // 3 62
        data[7][i++]= new Point3f(-1.1f, 0.0f,-0.3f); // 4 63
        System.out.println("end polygon; total vertex count: "+i);
                                                                    
        i = 0;
        data[8][i++]= new Point3f(-1.3f, 0.0f,-0.3f); // 0 64
        data[8][i++]= new Point3f(-1.3f,-0.3f,-0.3f); // 1 65
        data[8][i++]= new Point3f(-1.3f,-0.3f,0.3f); // 2 66
        data[8][i++]= new Point3f(-1.3f, 0.0f,0.3f); // 3 67
        data[8][i++]= new Point3f(-1.3f, 0.0f,-0.3f); // 4 68
        System.out.println("end polygon; total vertex count: "+i);

        return data;
    }
            

    Appearance createMaterialAppearance(){

        Appearance materialAppear = new Appearance();
        PolygonAttributes polyAttrib = new PolygonAttributes();
        polyAttrib.setPolygonMode(polyAttrib.POLYGON_FILL);
        //polyAttrib.setBackFaceNormalFlip(true);
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        materialAppear.setPolygonAttributes(polyAttrib);


        return materialAppear;
    }


    /////////////////////////////////////////////////
    //
    // create scene graph branch group
    //
    public BranchGroup createSceneGraph() {

        System.out.println("\n --- geometry debug information --- \n");

        int[] stripCount = {17,17,5,5,5,5,5,5,5};

        ////////////////////////////////////////////////////////////////////////////
        //       trying to set the colors of the gi vertices  
        int numberOfTiles = 9;
        Color3f[][] tileColors = new Color3f[numberOfTiles][];
        for (int i=0;i<numberOfTiles;i++) {
            tileColors[i] = new Color3f[stripCount[i]];
            for (int j=0;j<stripCount[i];j++) {
                tileColors[i][j] = new Color3f(1.0f,0.0f,0.0f);
            }
        }

        ///////////////////////////////////////////////////////////////////////////////


        Point3f[][] coordinateData = null;
        coordinateData = createCoordinateData();
        

        GeometryInfo[] gi = new GeometryInfo[9];
        Triangulator tr = new Triangulator();
        NormalGenerator ng = new NormalGenerator();
        Stripifier st = new Stripifier();

        for (int i=0;i<stripCount.length;i++) {
            System.out.println("shape number: "+i);
            gi[i] = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
            
            gi[i].setCoordinates(coordinateData[i]);
            gi[i].setStripCounts(new int[]{stripCount[i]});

            gi[i].setColors(tileColors[i]);
            
            System.out.println("begin triangulation");
            tr.triangulate(gi[i]);
            System.out.println("  END triangulation");
            gi[i].recomputeIndices();
            
            ng.generateNormals(gi[i]);
            gi[i].recomputeIndices();
            st.stripify(gi[i]);
            gi[i].recomputeIndices();

        }


        Shape3D part = new Shape3D();
        part.setAppearance(createMaterialAppearance());
        //part.setAppearance(createWireFrameAppearance());


        part.setPickable(true);
                
        part.setCapability(part.ALLOW_GEOMETRY_READ);
        part.setCapability(part.ALLOW_BOUNDS_READ);
        
        System.out.println("Stuffing the geometry arrays into the shape...");
        part.setGeometry(gi[0].getGeometryArray());
        for (int i=1;i<gi.length;i++) {
            part.addGeometry(gi[i].getGeometryArray());
        }

        System.out.println("Setting the geometries to allow intersect testing...");
        System.out.println("number of geometries: "+part.numGeometries());
        for (int i=0;i<part.numGeometries();i++) {
            part.getGeometry(i).setCapability(Geometry.ALLOW_INTERSECT);
            part.getGeometry(i).setCapability(GeometryArray.ALLOW_FORMAT_READ);
            part.getGeometry(i).setCapability(GeometryArray.ALLOW_COORDINATE_READ);
            part.getGeometry(i).setCapability(GeometryArray.ALLOW_COUNT_READ);
            part.getGeometry(i).setCapability(GeometryArray.ALLOW_REF_DATA_READ);
            part.getGeometry(i).setCapability(GeometryArray.ALLOW_COLOR_WRITE);

        }
        System.out.println("Finished stuffing and setting properties...");

        /////////////////////////////

        BranchGroup contentRoot = new BranchGroup();

        // Create the transform group node and initialize it to the
        // identity. Add it to the root of the subgraph.
        TransformGroup objSpin = new TransformGroup();
        objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        contentRoot.addChild(objSpin);

        objSpin.addChild(part);
        ////////////////////////

        Point3f[] lineCoordinates = new Point3f[69];
        int k=0;
        for (int i=0;i<coordinateData.length;i++) {
            for (int j=0;j<coordinateData[i].length;j++) {
                lineCoordinates[k++] = coordinateData[i][j];
            }
        }

        LineStripArray lineArray = new LineStripArray(69, LineArray.COORDINATES, stripCount); //*****
        lineArray.setCoordinates(0, lineCoordinates);
        Appearance blueColorAppearance = new Appearance();
        ColoringAttributes blueColoring = new ColoringAttributes();
        blueColoring.setColor(0.0f, 0.0f, 1.0f);
        blueColorAppearance.setColoringAttributes(blueColoring);
        LineAttributes lineAttrib = new LineAttributes();
        lineAttrib.setLineWidth(2.0f);
        blueColorAppearance.setLineAttributes(lineAttrib);
        Shape3D lines = new Shape3D(lineArray, blueColorAppearance);
        lines.setPickable(false);
        objSpin.addChild(lines);
        Alpha rotationAlpha = new Alpha(-1, 32000);
  
        RotationInterpolator rotator =
                 new RotationInterpolator(rotationAlpha, objSpin);
 
        // a bounding sphere specifies a region a behavior is active
        // create a sphere centered at the origin with radius of 100
        BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
        rotator.setSchedulingBounds(bounds);
        objSpin.addChild(rotator);

        /*
        DirectionalLight lightD = new DirectionalLight();
        lightD.setDirection(new Vector3f(0.0f,-0.7f,-0.7f));
        lightD.setInfluencingBounds(bounds);
        contentRoot.addChild(lightD);

        AmbientLight lightA = new AmbientLight();
        lightA.setInfluencingBounds(bounds);
        contentRoot.addChild(lightA);

        /*
        Background background = new Background();
        background.setColor(1.0f, 1.0f, 1.0f);
        background.setApplicationBounds(bounds);
        contentRoot.addChild(background);
        */

        ///////////////////////////////////////////////////////////////
        // pick behavior:
        picker = new PickBehavior(contentRoot,part,canvas3D, bounds, tileColors);
        //picker.setBounds(bounds);

        contentRoot.addChild(picker);




        // Let Java 3D perform optimizations on this scene graph.
        //contentRoot.compile();

        return contentRoot;
    } // end of CreateSceneGraph method of MobiusApp

    // Create a simple scene and attach it to the virtual universe

    public Small() {
        setLayout(new BorderLayout());
        canvas3D = new Canvas3D(null);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();

        // SimpleUniverse is a Convenience Utility class
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
    } // end of Small constructor

    //  The following allows this to be run as an application
    //  as well as an applet

    public static void main(String[] args) {
        Frame frame = new MainFrame(new Small(), 256, 256);
    } // end of main method of MobiusApp

} // end of class Small


// ***************************************************************************************

class PickBehavior extends Behavior {
    Shape3D part;                 
    Shape3D shape = null;
    private WakeupCriterion AWTEventCondition;
    WakeupOnAWTEvent awaken;
    AWTEvent[] eventArray;
    Canvas3D canvas;
    BranchGroup root;
    Color3f[][] tileColors;

    public PickBehavior(BranchGroup root, Shape3D part, Canvas3D canvas, Bounds bounds, Color3f[][] tileColors) {
        super();
        this.setSchedulingBounds(bounds);
        this.part = part;
        this.tileColors = tileColors;
        this.canvas = canvas;
        this.root = root;
        // this object's processStimulus method will be called by the system when a "MOUSE_PRESSED" event occurs
        AWTEventCondition = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
    }

    public void initialize() {
        // set initial wakeup condition
        this.wakeupOn(AWTEventCondition);
    }

    public void processStimulus(Enumeration criteria) {
        
        System.out.println("\n...got a click...");

        // get source of event event
        awaken = (WakeupOnAWTEvent)criteria.nextElement();
        // get the array of AWTEvents
        eventArray = awaken.getAWTEvent();
        // pull out the mouse event that triggered the stimulus  
        MouseEvent currentEvent = (MouseEvent)eventArray[0];


        PickCanvas pickCanvas = new PickCanvas(canvas, root);
        pickCanvas.setTolerance(0);
        pickCanvas.setShapeLocation(currentEvent);
        pickCanvas.setMode(PickTool.GEOMETRY);

        PickResult result = pickCanvas.pickClosest();
        //PickResult resultArray[] = pickCanvas.pickAll();

        //System.out.println("length of resultArray: "+resultArray.length);

//System.out.println("pickshape is: "+pickCanvas.getPickShape().toString());


        if (result != null) {
System.out.println("number of intersections: "+ result.numIntersections());
System.out.println("Number of GeometryArrays returned: "+result.numGeometryArrays());
    


            if (result.numIntersections() > 0) {
                PickIntersection pickIntersection = result.getIntersection(0);
                int index = pickIntersection.getGeometryArrayIndex();
System.out.println("Index of picked shape: "+ index);

                int vertexCount = ((GeometryArray)part.getGeometry(index)).getVertexCount();

System.out.println("length of color array in the Geometry returned:"+ vertexCount);

                for (int i=0;i<vertexCount;i++) {
                    //newPanelColor = new Color3f(0.0f,1.0f,0.0f);
                    ((GeometryArray)part.getGeometry(index)).setColor(i,new Color3f(0.0f,1.0f,0.0f));
                }

            }
        } 
        this.wakeupOn(AWTEventCondition);
    }

}                                




