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 LineParticle extends Object
        {
        int size = 1;
        int age = 0;
        int trailers = 1;
        int oobCount = 0;
        Vector3d location;
        Vector3d direction;
        Point3d[] vertexes;
        int weight = 1;
        Appearance appearance;
        private Group group  = new Group();
        Transform3D oofst;
        TransformGroup[] objOffset;
        LineArray lineArray;
        Shape3D shape;

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

        private void makeLineParticle()
                {
                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
                System.out.println("    LINE_PARTICLE -- Vertex Count = " + trailers);
                lineArray = new LineArray((int)trailers,LineArray.COORDINATES | LineArray.COLOR_3);

                lineArray.setCapability(GeometryArray.ALLOW_COORDINATE_READ);
                lineArray.setCapability(GeometryArray.ALLOW_COORDINATE_WRITE);
                lineArray.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
                
                for(int ver=0;ver<trailers;ver++)
                        {
//                      lineArray.setColor(ver,diffuseOrange);
                        lineArray.setColor(ver,black);
                        };


                lineArray.setCoordinates(0,vertexes);

                shape = new Shape3D(lineArray);
                shape.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
                shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);

                thisOffset.addChild(shape);
                // 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++)
                        {
                        Point3d fill = new Point3d();
                        try
                                {
                                if(trailers==trail+2)
                                        {
                                        lineArray.getCoordinate(trail+1,fill);
                                        lineArray.setCoordinate(trail,fill);
                                        }
                                else
                                        {
                                        lineArray.getCoordinate(trail+2,fill);
                                        lineArray.setCoordinate(trail,fill);
                                        };
                                }
                        catch(Exception e)
                                {
                                System.out.println("    LINE_PARTICLE -- Exception  " + e);
                                for(int errorPrint = 0; errorPrint<trailers;errorPrint++)
                                        {
                                        Point3d ePoint = new Point3d();
                                        lineArray.getCoordinate(errorPrint,fill);
                                        System.out.println("    LINE_PARTICLE -- Coord #" + errorPrint + "     =" + ePoint.x);
                                        };
                                };

                        
//                      fill.get(vertexes[trail+1]);
//                      vertexes[trail].set(fill);
                        };

                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);
                Point3d loc = new Point3d(location.x,location.y,location.z);

                lineArray.setCoordinate(trailers-1,loc);
                shape.setGeometry(lineArray);
                };


        // 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;
                };
        }
