import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.*;
import java.util.Enumeration;

public class Particle extends Object
        {
        int size = 1;
        int age = 0;
        int trailers = 1;
        int oobCount = 0;
        Vector3d location;
        Vector3d direction;
        Vector3d[] vertexes;
        int weight = 1;
        Appearance appearance;
        private Group group  = new Group();
        Transform3D oofst;
        TransformGroup[] objOffset;
        TransformGroup trailGroup;

        public Particle(Vector3d v1,Vector3d v2, float s, int t) 
                {
                location = v1;
                trailers = t;
                vertexes = new Vector3d[trailers];
                for(int v = 0; v<trailers;v++)
                        {
                        vertexes[v] = new Vector3d(location);
                        };
                direction = v2;
                size = (int) s;
                makeParticle();
                };

        private TransformGroup trailParticlel()
                {               
                objOffset = new TransformGroup[trailers];
                trailGroup = new TransformGroup();

                for(int tp=0;tp<trailers;tp++)
                        {
                        objOffset[tp] = new TransformGroup();
                        objOffset[tp].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
                        objOffset[tp].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                        // create the shape objects and add them to the inner transform
                        Box bx = new Box(size,size,size, appearance); 
                        objOffset[tp].addChild(bx);
                        // add the inner transform
                        oofst = new Transform3D();
                        oofst.setTranslation(vertexes[tp]);
                        objOffset[tp].setTransform(oofst);
                        BoundingSphere bounds = new BoundingSphere(new Point3d( location.x,location.y,location.z), size*2);
                        BoundingLeaf boundingLeaf = new BoundingLeaf(bounds);
                        objOffset[tp].addChild(boundingLeaf);

                        // add the transforms to the trailer group
                        trailGroup.addChild(objOffset[tp]);
                        };
                return trailGroup;
                };

        private void makeParticle()
                {
                Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
                Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
                Color3f diffuseWhite = new Color3f(0.75f, 0.75f, 0.75f);
                Color3f specularWhite = new Color3f(1.0f,1.0f,1.0f);
                Color3f ambientWhite = new Color3f(0.15f, 0.15f, 0.15f);
                Color3f ambientOrange = new Color3f(0.25f, 0.15f, 0.0f);
                Color3f diffuseOrange = new Color3f(0.75f, 0.65f, 0.3f);
                Color3f specularOrange = new Color3f(1.0f, 0.875f, 0.6f);

                Material orangeMaterial = new Material(ambientOrange, black, diffuseOrange, specularOrange, 100.0f);
                orangeMaterial.setLightingEnable(true);
                appearance = new Appearance();
                appearance.setMaterial(orangeMaterial);

                BranchGroup branchGroup = new BranchGroup();

                
                TransformGroup thisOffset = new TransformGroup();
                BoundingSphere bounds = new BoundingSphere(new Point3d(0,0,0), 250);
                thisOffset.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
                thisOffset.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                // create the shape objects and add them to the inner transform

                thisOffset.addChild(trailParticlel());
                // add the inner transform
                oofst = new Transform3D();
//              oofst.setTranslation(location);
                thisOffset.setTransform(oofst);
                BoundingLeaf boundingLeaf = new BoundingLeaf(bounds);
                thisOffset.addChild(boundingLeaf);
                branchGroup.addChild(thisOffset);

                // Let Java 3D perform optimizations on this scene graph.
                branchGroup.compile();
                // add this to the output group
                group.addChild(branchGroup);
                };

        public void update()
                {
                for(int trail = 0;trail<trailers-1;trail++)
                        {
                        vertexes[trail].set(vertexes[trail+1].x,vertexes[trail+1].y,vertexes[trail+1].z);
                        };

                if(location.length() > 105)
                        {
                        if(location.length() > 105+3*size)
                                {
                                location.normalize();
                                }
                        else
                                {
                                if(oobCount<1)
                                        {
                                        direction.negate();
                                        }
                                else
                                        {
                                        if(oobCount > 4)
                                                {
                                                oobCount=0;
                                                }
                                        else
                                                {
                                                oobCount++;
                                                };
                                        };
                                };
                        }
                else
                        {
                        oobCount=0;
                        };
                direction.scale(size*2);
                location.add(direction);
                vertexes[trailers-1].set(location);

                for(int tp = 0;tp<trailers;tp++)
                        {
                        oofst = new Transform3D();
                        oofst.setTranslation(vertexes[tp]);
                        objOffset[tp].setTransform(oofst);
                        };
                };


        // method to return the branch group to the main program when needed
        public Group getChild() 
                {
                return group;
                };

        public Vector3d getLocation()
                {
                return location;
                };
        public Vector3d getDirection()
                {
                return direction;
                };
        public void setDirection(Vector3d avgDir, Vector3d avgLoc)
                {
                Vector3d flockDist = new Vector3d();
                flockDist.sub(location,avgLoc);
                double fd = flockDist.length();

                Vector3d flockDir = new Vector3d();
                flockDir.sub(direction,avgDir);
                double fdir = flockDir.length();


                double percentDist = fd/150;
                double percentDir = fdir/3;

                double adjustmentPercent = percentDist + percentDir;

                avgDir.scale(adjustmentPercent);

                Vector3d randVec = createNormalisedVector();
                randVec.scale(0.5);
                avgDir.add(randVec);

                direction.add(avgDir);
                direction.normalize();
                };
        public Vector3d createNormalisedVector()
                {
                double xpos = ((double)Math.random()-0.5)*2;
                double ypos = ((double)Math.random()-0.5)*2;
                double zpos = ((double)Math.random()-0.5)*2;
                Vector3d randomTransform = new Vector3d(xpos,ypos,zpos);
                randomTransform.normalize();
                return randomTransform;
                };
        }
