import java.applet.*;import java.awt.*;
import java.awt.event.*;import java.io.*;import java.net.*;import Point3D;import Matrix3D;
import Canvas3D;
public class TestMatrix extends Applet
						implements ActionListener, 
								   AdjustmentListener,
								   TextListener 
{
    final static int CHUNKSIZE = 100;    Raster raster;    Image screen;    Point3D vertList[];    Point3D worldList[];    Point3D tranList[];    int vertices;    Matrix3D view;    Matrix3D model;
	float eyex, eyey, eyez;    float lookatx, lookaty, lookatz;    float upx, upy, upz;    float fov;
	
	boolean perspMode = false;
		Panel panel1, panel2, panel3;
	Scrollbar rotate_x, rotate_y, rotate_z, trans_x, trans_y, trans_z, scale_x, scale_y, scale_z, skew_x, skew_y, skew_z;	Label rotateLab, transLab, scaleLab, skewLab, fovLab;	Button ortho, persp;		TextField fovText;
	Canvas3D canvas;		
    public void init( )
    {
	
		//Set up GUI		this.setBackground(new Color(0x00000055));
		
		panel1 = new Panel();		panel2 = new Panel();
		panel3 = new Panel();		ortho = new Button("Orthographic");		persp = new Button("Perspective");				fovText = new TextField();		fovText.setEditable(true);
		canvas = new Canvas3D();
		canvas.setSize(400,400);
				panel1.setBackground(new Color(0x00000099));		panel2.setBackground(new Color(0x00000099));		panel3.setBackground(new Color(0x00000099));
		
		ortho.addActionListener(this);
		persp.addActionListener(this);
		fovText.addTextListener(this);				rotateLab = new Label("Rotate", Label.CENTER);
		transLab = new Label("Translate", Label.CENTER);		scaleLab = new Label("Scale", Label.CENTER);
		skewLab = new Label("Skew", Label.CENTER);
		fovLab = new Label("FOV   ", Label.RIGHT);				rotateLab.setForeground(new Color(0xFFFFFFFF));		transLab.setForeground(new Color(0xFFFFFFFF));
		scaleLab.setForeground(new Color(0xFFFFFFFF));
		skewLab.setForeground(new Color(0xFFFFFFFF));		fovLab.setForeground(new Color(0xFFFFFFFF));
		
		fovText.setBackground(new Color(0xffffffff));
		fovText.setForeground(new Color(0xff000000));				rotate_x = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 360);		rotate_y = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 360);
		rotate_z = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 360);				trans_x = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 10);		trans_y = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 10);		trans_z = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 10);
				scale_x = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 100, 500);
		scale_y = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 100, 500);
		scale_z = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 100, 500);				skew_x = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 1000);		skew_y = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 1000);		skew_z = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 1000);
		
		rotate_x.addAdjustmentListener(this);
		rotate_y.addAdjustmentListener(this);
		rotate_z.addAdjustmentListener(this);		trans_x.addAdjustmentListener(this);		trans_y.addAdjustmentListener(this);		trans_z.addAdjustmentListener(this);
		scale_x.addAdjustmentListener(this);
		scale_y.addAdjustmentListener(this);
		scale_z.addAdjustmentListener(this);
		skew_x.addAdjustmentListener(this);
		skew_y.addAdjustmentListener(this);
		skew_z.addAdjustmentListener(this);				panel2.setLayout(new GridLayout(1,2));		panel2.add(ortho);		panel2.add(persp);				panel3.setLayout(new GridLayout(1,2));		panel3.add(fovLab);		panel3.add(fovText);
		Label temp = new Label();
		Label temp2 = new Label();
		Label temp3 = new Label();
		Label temp4 = new Label();				temp2.setBackground(new Color(0x00000099));
		panel1.setLayout(new GridLayout(21,1));
		panel1.add(temp);
		panel1.add(rotateLab);		panel1.add(rotate_x);
		panel1.add(rotate_y);
		panel1.add(rotate_z);
		panel1.add(transLab);		panel1.add(trans_x);		panel1.add(trans_y);		panel1.add(trans_z);
		panel1.add(scaleLab);
		panel1.add(scale_x);
		panel1.add(scale_y);
		panel1.add(scale_z);
		panel1.add(skewLab);
		panel1.add(skew_x);
		panel1.add(skew_y);
		panel1.add(skew_z);		panel1.add(temp3);		panel1.add(panel2);		panel1.add(temp4);
		panel1.add(panel3);		
		panel1.setSize(300,400);
		GridBagLayout gl = new GridBagLayout();		GridBagConstraints gb = new GridBagConstraints();
		
		setLayout(gl);				gb.gridx = GridBagConstraints.RELATIVE;
		gb.gridy = GridBagConstraints.RELATIVE;
		gb.fill = GridBagConstraints.BOTH;
		gb.anchor = GridBagConstraints.CENTER;				gb.gridheight = 1;
		gb.gridwidth = 10;
		gb.weightx = 0;		gb.weighty = 0;
		gl.setConstraints(canvas, gb);		add(canvas);				gb.gridheight = 1;
		gb.gridwidth = GridBagConstraints.RELATIVE;
		gb.weightx = 0.2;		gb.weighty = 0.2;
		gl.setConstraints(temp2, gb);		add(temp2);
		gb.gridheight = 1;
		gb.gridwidth = GridBagConstraints.REMAINDER;
		gb.weightx = 1;		gb.weighty = 1;
		gl.setConstraints(panel1, gb);		add(panel1);		

		//Set up view and model matrices
		
		raster = new Raster(400, 400);        raster.fill(getBackground());		screen = raster.toImage();
        eyex = 0;        eyey = 0;        eyez = -10;
        lookatx = 0;     lookaty = 0;     lookatz = 0;
        upx = 0;         upy = 1;         upz = 0;
        fov = 30;
        vertList = new Point3D[CHUNKSIZE];        vertices = 0;
        String filename = getParameter("datafile");        showStatus("Reading "+filename);        InputStream is = null;
        try {			is = new URL(getDocumentBase(), filename).openStream();            ReadInput(is);            is.close();        } catch (IOException e) {            showStatus("Error reading "+filename);        }
        worldList = new Point3D[vertList.length];        tranList = new Point3D[vertList.length];        for (int i = 0; i < vertices; i++) {            worldList[i] = new Point3D();            tranList[i] = new Point3D();        }
		
		               view = new Matrix3D(raster);
		        float t = (float) Math.sin(Math.PI*(fov/2)/180);		float s = (t*400)/400;            		view.perspective(-t, t, -s, s, -1, -200);		view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);       
		        model = new Matrix3D();        long time = System.currentTimeMillis();				updateModel();				time = System.currentTimeMillis() - time;        showStatus("Time = "+time+" ms");
		
    }

    private double getNumber(StreamTokenizer st) throws IOException    {        if (st.nextToken() != StreamTokenizer.TT_NUMBER) {            System.err.println("ERROR: line "+st.lineno()+": expected number");            throw new IOException(st.toString());        }        return st.nval;    }
    public void ReadInput(InputStream is) throws IOException    {	    StreamTokenizer st = new StreamTokenizer(is);    	st.commentChar('#');        scan: while (true) {	        switch (st.nextToken()) {	          default:		        break scan;	          case StreamTokenizer.TT_WORD:		        if (st.sval.equals("v")) {		            float x = (float) getNumber(st);		            float y = (float) getNumber(st);		            float z = (float) getNumber(st);		            if (vertices == vertList.length) {		                Point3D newList[] = new Point3D[vertList.length+CHUNKSIZE];                        System.arraycopy(vertList, 0, newList, 0, vertList.length);		                vertList = newList;		            }					vertList[vertices++] = new Point3D(x, y, z);		        } else			    if (st.sval.equals("eye")) {                    eyex = (float) getNumber(st);                    eyey = (float) getNumber(st);		            eyez = (float) getNumber(st);			    } else			    if (st.sval.equals("look")) {                    lookatx = (float) getNumber(st);                    lookaty = (float) getNumber(st);		            lookatz = (float) getNumber(st);			    } else			    if (st.sval.equals("up")) {                    upx = (float) getNumber(st);                    upy = (float) getNumber(st);		            upz = (float) getNumber(st);			    } else			    if (st.sval.equals("fov")) {                    fov = (float) getNumber(st);
					fovText.setText((new Float(fov)).toString());
					perspMode = true;			    } else {                    System.err.println("ERROR: line "+st.lineno()+": unexpected token :"+st.sval);                    break scan;			    }			    break;	        }	        if (vertices % 100 == 0) showStatus("vertices = " + vertices);	    }        is.close();	    if (st.ttype != StreamTokenizer.TT_EOF) {	        throw new IOException(st.toString());	    }
		
	}

    public void paint(Graphics g)    {		canvas.repaint(screen);		    }

    public void update(Graphics g)    {		paint(g);    }

    float v0x, v0y, v0z;
    public boolean mouseDown_back(Event e, int x, int y)    {
		
        if (e.metaDown()) {            showStatus("Resetting model matrix");            model.loadIdentity();        }
        v0x = (float) (x - (size().width / 2));        v0y = (float) ((size().height / 2) - y);        v0z = (float) size().width;
		float l0 = (float) (1 / Math.sqrt(v0x*v0x + v0y*v0y + v0z*v0z));        v0x *= l0;        v0y *= l0;        v0z *= l0;
	    return true;    }
	
	
	public void updateModel() {
		
		updateModel(rotate_x.getValue(), rotate_y.getValue(), rotate_z.getValue(),
					trans_x.getValue(), trans_y.getValue(), trans_z.getValue(),					scale_x.getValue()/100.0f, scale_y.getValue()/100.0f, scale_z.getValue()/100.0f,					skew_x.getValue()/100, skew_y.getValue()/100, skew_z.getValue()/100);
	}
	
	public void updateModel(float angle_x, float angle_y, float angle_z,							float trans_x, float trans_y, float trans_z,
							float scale_x, float scale_y, float scale_z,
							float skew_x, float skew_y, float skew_z)    {		
		model.loadIdentity();				model.rotate(1.0f, 0.0f, 0.0f, (float)(angle_x*Math.PI/180.0f));
		model.rotate(0.0f, 1.0f, 0.0f, (float)(angle_y*Math.PI/180.0f));
		model.rotate(0.0f, 0.0f, 1.0f, (float)(angle_z*Math.PI/180.0f));
		
		model.translate(trans_x, trans_y, trans_z);				model.scale(scale_x, scale_y, scale_z);
		
		model.skew(skew_x, skew_y, skew_z);
				model.transform(vertList, worldList, 0, vertices);		
		displayModel();
		
	}
	
	public void displayModel() {		        view.transform(worldList, tranList, 0, vertices);
        DrawObject();
		
		screen = raster.toImage();        repaint();
    }

    void DrawObject()    {        int ix, iy;        int w, h;
		w = raster.width;        h = raster.height;
		raster.fill(getBackground());
        for (int i = 0; i < vertices; i++) {            ix = (int) tranList[i].x;            iy = (int) tranList[i].y;            if (ix >= 0 && iy >= 0 && ix < w && iy < h)				raster.setPixel(0xffffffff, ix, iy);
        }    }
	
	

	public void adjustmentValueChanged (AdjustmentEvent e) 	{
		System.out.println("AdjustmentEvent!");				if ( (e.getAdjustable() == rotate_x)
			|| (e.getAdjustable() == rotate_y)			 || (e.getAdjustable() == rotate_z) ) 			showStatus("Rotating...");
		
		if ( (e.getAdjustable() == scale_x)
			|| (e.getAdjustable() == scale_y)			 || (e.getAdjustable() == scale_z) ) 			showStatus("Scaling...");
		if ( (e.getAdjustable() == skew_x)
			|| (e.getAdjustable() == skew_y)			 || (e.getAdjustable() == skew_z) ) 			showStatus("Skewing...");
		if ( (e.getAdjustable() == trans_x)
			|| (e.getAdjustable() == trans_y)			 || (e.getAdjustable() == trans_z) ) 			showStatus("Translating...");						updateModel();
		
			}
	
	
	public void actionPerformed (ActionEvent e) {				float t = (float) Math.sin(Math.PI*(fov/2)/180);		float s = (t*400)/400;
		
		showStatus("Switched to " + e.getActionCommand() + " view"); 
				view = new Matrix3D(raster);
		
		if (e.getActionCommand() == ortho.getLabel()) {			perspMode = false;
			view.orthographic(-10*t, 10*t, -10*s, 10*s, -1, -200);			view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);		}

		if (e.getActionCommand() == persp.getLabel()) {
			perspMode = true;
			view.perspective(-t, t, -s, s, -1, -200);			view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
		}
		

		updateModel();		
	}
	
	public void textValueChanged (TextEvent e) {				showStatus ("Changed FOV value to: " + fovText.getText());		if (fovText.getText() != null)			fov = (new Float(fovText.getText())).floatValue();
		
		if (perspMode) {			float t = (float) Math.sin(Math.PI*(fov/2)/180);			float s = (t*400)/400;
			view = new Matrix3D(raster);
			view.perspective(-t, t, -s, s, -1, -200);			view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
			updateModel();
		}		
	}		

}





