/*
  Point cloud viewer, hacked together from HelloUniverse and ObjLoad
  Ethan Gold <etgold@cs.columbia.edu>
  updated to multiple clouds & colors 2/9/01
 */

//import com.sun.j3d.loaders.objectfile.ObjectFile;
//import com.sun.j3d.loaders.ParsingErrorException;
//import com.sun.j3d.loaders.IncorrectFormatException;
//import com.sun.j3d.loaders.Scene;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.io.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import java.util.StringTokenizer;
import java.util.Vector;

public class PointCloudsShowCenter extends Applet {

  private float scalefactor = 1.0f;
    private boolean spin = false;
    private boolean noTriangulate = false;
    private boolean noStripify = false;
    private double creaseAngle = 60.0;
    //private String filename = null;
    private Vector filenames;
    private Color3f cloudColors[];
    private String cloudColorNames[];
    private int MAXCOLORS = 5;

    //--------------------------------------
    // DO MAIN WORK HERE
    //--------------------------------------
    public BranchGroup createSceneGraph(String args[]) {
	// Create the root of the branch graph
	BranchGroup objRoot = new BranchGroup();


	// Create the transform group node and initialize it to the
	// identity.  Enable the TRANSFORM_WRITE capability so that
	// our behavior code can modify it at runtime.  Add it to the
	// root of the subgraph.
	TransformGroup objTrans = new TransformGroup();
	objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
	objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
	objRoot.addChild(objTrans);


	PointArray vertices = null;
	int numpoints, maxpoints;
	float cf1, cf2, cf3;
	String filename;

	System.out.println("loading "+filenames.size()+" files");

	for (int k=0; k<filenames.size(); k++) {

	    cf1 = cf2 = cf3 = 0.0f;
	    numpoints = 0;
	    maxpoints = 0;
	    filename = (String)(filenames.elementAt(k));
	    try {
		//s = f.load(filename);
		BufferedReader br =
		    new BufferedReader(new FileReader(filename));
		String dataline = null;
		float f1, f2, f3, f4;
		Point3f p3f;
		int lineno = 1;
		int headlines = 0;
		StringTokenizer strtok;
		if (br.ready()) {
		    // first line is x resolution, second is y resolution
		    int xres, yres;
		    try {
		    strtok = new StringTokenizer(br.readLine());
		    xres = Integer.parseInt(strtok.nextToken());
		    strtok = new StringTokenizer(br.readLine());
		    lineno++;
		    if (strtok.countTokens() == 1) {
			yres = Integer.parseInt(strtok.nextToken());
			lineno++;
			maxpoints = xres*yres;
			headlines = 2;
		    } else { // cyrax format w/ all points on first line
			System.out.println("cyrax format detected");
			maxpoints = xres;
			headlines = 1;
		    }
		    } catch (NumberFormatException nfd) {
			System.out.println("Error reading dimensions of file "+filename);
			continue;
		    }
		    vertices = new PointArray(maxpoints*3, PointArray.COORDINATES);
		} else {
		    System.out.println("File not ready. is it empty??");
		    System.exit(1); // can't go any farther
		}
		
		while (br.ready() && (lineno-headlines < maxpoints)) {
		    //System.out.println(dataline);
		    strtok = new StringTokenizer(br.readLine());
		    lineno++;
		    if (strtok.countTokens() != 4 ) {
			System.out.println("wrong number of fields on line "+lineno);
			continue; // back to the top of the while loop
		    } else {
			try {
			  //scale
			    f1 = Float.parseFloat(strtok.nextToken()) / scalefactor;
			    f2 = Float.parseFloat(strtok.nextToken()) / scalefactor;
			    f3 = Float.parseFloat(strtok.nextToken()) / scalefactor;
			    if ((0.f == f1)&&(0.f == f2)&&(0.f == f3))
				continue;
			    // if we get here our data is good
			    //p3f = new Point3f(f1, f2, f3+50);
			    p3f = new Point3f(f1, f2, f3);
			    vertices.setCoordinate(lineno-2, p3f);
			    cf1 += f1; cf2 += f2; cf3 += f3;
			    numpoints++;
			}
			catch (Exception e) {
			    System.out.println("bad numerical data on line "+lineno+": "+e);
			    continue; // back to the top of the while loop
			}
		    } // end reading data lines
		    
		} // end reading while
		System.out.println("read "+(lineno-headlines)+" input vertices from "+filename);
		// speed everything up by throwing away zero points
		/*
		  System.out.println("numpoints: "+numpoints
		  +" lines: "+(lineno-headlines)
		  +" maxpoints: "+maxpoints);
		*/

		// finish averaging the centroid
		cf1 = cf1/(float)numpoints;
		cf2 = cf2/(float)numpoints;
		cf3 = cf3/(float)numpoints;
		System.out.println("centroid: "+cf1+", "+cf2+", "+cf3);

		if (numpoints < (lineno-headlines)) {
		    int j = 0;
		    int i = 0;
		    p3f = new Point3f();
		    PointArray tmpvertices = new PointArray(numpoints*3, PointArray.COORDINATES);
		    for (i=0; i<(lineno-headlines); i++) {
			vertices.getCoordinate(i, p3f);
			if ((p3f.x != 0.f) || (p3f.y != 0.f) || (p3f.z != 0.f)) {
			  /*
			  //subtract the centroid
			  p3f.x -= cf1; p3f.y -= cf2; p3f.z -= cf3;
			  */

			    tmpvertices.setCoordinate(j, p3f);
			    j++;
			}
		    }
		    vertices = tmpvertices;
		    System.out.println("culled "+(i-j)+" empty vertices from "+filename);
		}
	    } // end try
	    catch (Exception e) {
		System.err.println(e);
		System.exit(1);
	    }




	    /*
	    // finish averaging the centroid
	    cf1 = cf1/(float)numpoints;
	    cf2 = cf2/(float)numpoints;
	    cf3 = cf3/(float)numpoints;
	    */
	    /*
	      Transform3D newt = new Transform3D();
	      Vector3f dotrans = new Vector3f(-cf1, -cf2, cf3);
	      objTrans.getTransform(newt);
	      newt.setTranslation(dotrans);
	      objTrans.setTransform(newt);
	      System.out.println("centroid: "+cf1+", "+cf2+", "+cf3);
	    */
	    
	    Shape3D cloud = new Shape3D(vertices);
	    cloud.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
	    Appearance ap = new Appearance();
	    ap.setColoringAttributes(new ColoringAttributes(cloudColors[k%cloudColors.length],
							    ColoringAttributes.FASTEST));
	    cloud.setAppearance(ap);


	    objTrans.addChild(cloud);
	    //objTrans.addChild(s.getSceneGroup());
	    System.out.println(filename+" is "+cloudColorNames[k%MAXCOLORS]);
	} // end loading files

	BoundingSphere bounds =
	    new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000.0);

	// Create the rotate behavior node
	MouseRotate behavior = new MouseRotate();
	behavior.setTransformGroup(objTrans);
	objTrans.addChild(behavior);
	behavior.setSchedulingBounds(bounds);
	
	// Create the zoom behavior node
	MouseZoom behavior2 = new MouseZoom();
	behavior2.setTransformGroup(objTrans);
	objTrans.addChild(behavior2);
	behavior2.setSchedulingBounds(bounds);
	
	// Create the translate behavior node
	MouseTranslate behavior3 = new MouseTranslate();
	behavior3.setTransformGroup(objTrans);
	objTrans.addChild(behavior3);
	behavior3.setSchedulingBounds(bounds);

        // Set up the background
        Color3f bgColor = new Color3f(0.05f, 0.05f, 0.5f);
        Background bgNode = new Background(bgColor);
        bgNode.setApplicationBounds(bounds);
        objRoot.addChild(bgNode);

        // Set up the ambient light
        Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
        AmbientLight ambientLightNode = new AmbientLight(ambientColor);
        ambientLightNode.setInfluencingBounds(bounds);
        objRoot.addChild(ambientLightNode);

        // Set up the directional lights
        Color3f light1Color = new Color3f(1.0f, 1.0f, 0.9f);
        Vector3f light1Direction  = new Vector3f(4.0f, -7.0f, -12.0f);
        Color3f light2Color = new Color3f(0.3f, 0.3f, 0.4f);
        Vector3f light2Direction  = new Vector3f(-6.0f, -2.0f, -1.0f);

        DirectionalLight light1
            = new DirectionalLight(light1Color, light1Direction);
        light1.setInfluencingBounds(bounds);
        objRoot.addChild(light1);

        DirectionalLight light2
            = new DirectionalLight(light2Color, light2Direction);
        light2.setInfluencingBounds(bounds);
        objRoot.addChild(light2);

	return objRoot;
    }

    private void usage()
    {
	System.out.println(
			   "Usage: java PointCloudsShowCenter pts_file [pts file] ...");
	System.exit(0);
    } // End of usage

    

    //-------------------------------------------
    // CONSTRUCTOR
    //-------------------------------------------
    public PointCloudsShowCenter(String args[]) {
        if (args.length == 0) {
	    usage();
	} else {
	    filenames = new Vector();
	    for (int i = 0 ; i < args.length ; i++) {
		if (args[i].startsWith("-")) {
		    if (args[i].equals("-s")) {
		      //spin = true;
		      scalefactor = (new Float(args[++i])).floatValue();
		    } else if (args[i].equals("-n")) {
			noTriangulate = true;
		    } else if (args[i].equals("-t")) {
			noStripify = true;
		    } else if (args[i].equals("-c")) {
			if (i < args.length - 1) {
			    creaseAngle = (new Double(args[++i])).doubleValue();
			} else usage();
		    } else {
			System.err.println("Argument '" + args[i] + "' ignored.");
		    }
		} else {
		    filenames.addElement(args[i]);
		}
	    }
	}

	if (0 == filenames.size()) {
	    usage();
	}

	cloudColors = new Color3f[MAXCOLORS];
	cloudColors[0] = new Color3f(0.8f, 0.8f, 0.8f);
	cloudColors[1] = new Color3f(0.8f, 0.2f, 0.2f);
	cloudColors[2] = new Color3f(0.2f, 0.8f, 0.2f);
	cloudColors[3] = new Color3f(0.2f, 0.2f, 0.8f);
	cloudColors[4] = new Color3f(0.8f, 0.2f, 0.8f);
	cloudColorNames = new String[MAXCOLORS];
	cloudColorNames[0] = new String("white");
	cloudColorNames[1] = new String("red");
	cloudColorNames[2] = new String("green");
	cloudColorNames[3] = new String("blue");
	cloudColorNames[4] = new String("purple");

	setLayout(new BorderLayout());
	Canvas3D c = new Canvas3D(null);
	add("Center", c);

	// Create a simple scene and attach it to the virtual universe
	BranchGroup scene = createSceneGraph(args);
	SimpleUniverse u = new SimpleUniverse(c);
        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        u.getViewingPlatform().setNominalViewingTransform();
	c.getView().setBackClipDistance(100.0);
	scene.compile();
	u.addBranchGraph(scene);
    }



    //
    // The following allows PointCloudSShow to be run as an application
    // as well as an applet
    //
    public static void main(String[] args) {
	new MainFrame(new PointCloudsShowCenter(args), 700, 700);
    }
}
