import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.awt.event.*;
import java.lang.*;

public class PersCubeTestMatrix extends Applet implements MouseListener, MouseMotionListener {
    final static int CHUNKSIZE = 100;
    Raster raster;
    Image screen;
    Point3D vertList[];
    Point3D worldList[];
    Point3D tranList[];
    Point3D axesList[];
    Point3D tranAxesList[];

    int vertices;
    int axesVtcs;
    Matrix3D view; 
    Matrix3D axesView;
    Matrix3D model;

    float eyex, eyey, eyez;
    float lookatx, lookaty, lookatz;
    float upx, upy, upz;
    float fov;

    public void init( )
    {
        raster = new Raster(getSize().width, getSize().height);
        raster.fill(getBackground());
        screen = raster.toImage(this);

        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;
        axesList = new Point3D[CHUNKSIZE];
        axesVtcs = 0;
	
	this.addMouseListener(this);
	this.addMouseMotionListener(this);

        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);
        }
// 	System.out.println("PRINTING ORIGINAL VERTICES" );
//         for (int i = 0; i < vertices; i++) {
// 	    System.out.println("Vertex no.= " + i );
// 	    System.out.println(" x= " + vertList[i].getCoord(0)+ " y= " + vertList[i].getCoord(1)+  " z= " + vertList[i].getCoord(2)+  " w= " + vertList[i].getCoord(3)+  " color= " + vertList[i].getColor() );   
//         }
	
        worldList = new Point3D[vertList.length];
        tranList = new Point3D[vertList.length];
        tranAxesList = new Point3D[axesList.length];
	
        for (int i = 0; i < vertices; i++) {
            worldList[i] = new Point3D();
            tranList[i] = new Point3D();
        }

	for (int i = 0; i < axesVtcs; i++) {
            tranAxesList[i] = new Point3D();
        }

        view = new Matrix3D(raster);
        axesView = new Matrix3D(raster);

	//VIEW.PERSPEC IS INITIALIZED WITH COORDS IN VIEW SYSTEM
        float t = (float) Math.sin(Math.PI*(fov/2)/180);
        float s = (t*getSize().height)/getSize().width;
	view.perspective(-t, t, -s, s, -1, -200);
	axesView.perspective(-t, t, -s, s, -1, -200);
	
// 	//VIEW.ORTHOG IS INITIALIZED WITH COORDS IN VIEW SYSTEM
//         float t = 3;
//         float s = 3;
// 	view.orthographic(-t, t, -s, s, -1, -200);
// 	axesView.orthographic(-t, t, -s, s, -1, -200);

// 	System.out.println("PRINTING VIEW MATRIX SP");
//         view.printCompMat();


// 	view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
// 	axesView.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
	setViewLine(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);


// 	System.out.println("PRINTING VIEW MATRIX SPE");
//         view.printCompMat();
        model = new Matrix3D();
        model.transform(vertList, worldList, 0, vertices);
// 	System.out.println("PRINTING MODELED VERTICES" );
// 	for (int i = 0; i < vertices; i++) {
// 	    System.out.println("Vertex no.= " + i );
// 	    System.out.println(" x= " + worldList[i].getCoord(0)+ " y= " + worldList[i].getCoord(1)+  " z= " + worldList[i].getCoord(2)+  " w= " + worldList[i].getCoord(3) );   
//         }
        view.transform(worldList, tranList, 0, vertices);

// 	System.out.println("PRINTING TRANFROMED VERTICES" );
// 	for (int i = 0; i < vertices; i++) {
// 	    System.out.println("Vertex no.= " + i );
// 	    System.out.println(" x= " + tranList[i].getCoord(0)+ " y= " + tranList[i].getCoord(1)+  " z= " + tranList[i].getCoord(2)+  " w= " + tranList[i].getCoord(3) );   
//         }
// 	System.out.println("PRINTING VIEW.TRN = " + view.getTrforms() );
// 	System.out.println("PRINTING MODEL.TRN = "+ model.getTrforms()  );
        DrawObject();
        screen = raster.toImage(this);

    }

    // define legal characters for our parser
    // The remainder of a line after a '#' is ignored
    // you might find this helpful for debugging
    private void configureParser(StreamTokenizer st) {
	st.commentChar('#');
    }



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


    // parse a packed integer in hex, octal, or decimal
    private int getColor(StreamTokenizer st) throws IOException {
	st.nextToken();
	int pix;
        if (st.sval.startsWith("0x") || st.sval.startsWith("0X")) {
	    pix = (int) Long.parseLong(st.sval.substring(2), 16);
	} else
            if (st.sval.startsWith("0") || st.sval.length() > 1) {
                pix = (int) Long.parseLong(st.sval.substring(1), 8);
            } else
                pix = Integer.parseInt(st.sval);
        return pix;
    }



    public void ReadInput(InputStream is) throws IOException
    {
	Reader r = new BufferedReader(new InputStreamReader(is));
	StreamTokenizer st = new StreamTokenizer(r);
	configureParser(st);
    scan: while (true) {
	switch (st.nextToken()) {
	default:
	    break scan;
	case StreamTokenizer.TT_WORD:
	    
	    if (st.sval.equals("c")) {
		float x = (float) getNumber(st);
		float y = (float) getNumber(st);
		float z = (float) getNumber(st);
		//int clr =  getColor(st);
		int clr = (int) getNumber(st) ;
		if (axesVtcs == axesList.length) {
		    Point3D newList[] = new Point3D[vertList.length+CHUNKSIZE];
		    System.arraycopy(axesList, 0, newList, 0, axesList.length);
		    axesList = newList;
		}
		axesList[axesVtcs++] = new Point3D(x, y, z, 1, clr);
	    } else
		
		if (st.sval.equals("v")) {
		    float x = (float) getNumber(st);
		    float y = (float) getNumber(st);
		    float z = (float) getNumber(st);
		    //int clr =  getColor(st);
		    int clr = (int) 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, 1, clr);
		} 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);
				} else {
				    System.err.println("ERROR: line "+st.lineno()+": unexpected token :"+st.sval);
				    break scan;
				}
	    break;
	}
	//if (vertices % 100 == 0)showStatus("vertices = " + (vertices+1));
	showStatus("vertices = " + vertices);
    }
        is.close();
	if (st.ttype != StreamTokenizer.TT_EOF) {
	    throw new IOException(st.toString());
	}
    }
    
    public void paint(Graphics g)
    {
        g.drawImage(screen, 0, 0, this);
    }

    public void update(Graphics g)
    {
        paint(g);
    }


    public void setViewLine(float eyex,float  eyey, float eyez, float lookatx, float  lookaty, float lookatz, float upx, float upy, float upz) {
	view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
	axesView.setTo(view);
	axesView.transform(axesList, tranAxesList, 0, axesVtcs);
   }


    float v0x, v0y, v0z;
    int state=0;
    int nRun = 1;

    public void mousePressed( MouseEvent e )
    {
	if( state==0 ) {
	    model.toOriginalMat();
// 	    System.out.println("IN STATE=" + state  );
	    model.scale(1,2,1);
	    model.transform(vertList, worldList, 0, vertices);
	    view.transform(worldList, tranList, 0, vertices);
	    DrawObject();
	    screen=raster.toImage(this);
	    repaint();
	    showStatus("scale: 1,2,1");
	    state++;
	    return;
	}
	if( state==1 ) {
	    model.toOriginalMat();
	    model.shear(1,0,0, 0,0,0);
	    model.transform(vertList, worldList, 0, vertices);
	    view.transform(worldList, tranList, 0, vertices);
	    DrawObject();
	    screen=raster.toImage(this);
	    repaint();
	    state++;
	    showStatus("shear: 1,0,0");
	    return;
	}
	if( state==2 ) {
	    model.toOriginalMat();
	    model.rotate(0,0,1, (float)(Math.PI)/3.0f);
	    model.transform(vertList, worldList, 0, vertices);
	    view.transform(worldList, tranList, 0, vertices);
	    DrawObject();
	    screen=raster.toImage(this);
	    repaint();
	    state++;
	    showStatus("rotate about z world axis");
	    return;
	}
	if( state==3 ) {
	    model.toOriginalMat();
// 	    System.out.println("IN STATE=" + state  );
// 	    System.out.println("PERFORMING ROTATION with model" );
	    model.translate(0.8f,0.5f,1);
// 	    System.out.println("PERFORMING TRANSFORMATION with model" );
	    model.transform(vertList, worldList, 0, vertices);
// 	    System.out.println("PERFORMING TRANSFORMATION with view" );
	    view.transform(worldList, tranList, 0, vertices);
	    DrawObject();
	    screen=raster.toImage(this);
	    repaint();
	    state++;
	    showStatus("translate :0.8,0.5,1");
	    return;
	}
	if( state==4 ) {
// 	    System.out.println("IN STATE=" + state  );
	    view.toOriginalMat();
	    
	    float t = (float) Math.sin(Math.PI*(fov/2)/180);
	    float s = (t*getSize().height)/getSize().width;
	    view.perspective(-t, t, -s, s, -1, -200);

// 	    float t = 3;
// 	    float s = 3;
// 	    view.orthographic(-t, t, -s, s, -1, -200);

 	    setViewLine(5.6f, 5.6f, 5.6f, lookatx, lookaty, lookatz, 0, 0, 1);
	    model.toOriginalMat();
	    model.rotate(0,1,0, (float)(Math.PI)/4.0f);
	    model.transform(vertList, worldList, 0, vertices);
	    view.transform(worldList, tranList, 0, vertices);

	    DrawObject();
	    screen=raster.toImage(this);
	    repaint();
	    state++;
	    showStatus("new eye, rotate about y world axis");
	    return;
	}
	if( state>4 ) {
	    ;
	}

    }

    public void mouseReleased( MouseEvent e ) { ; }
    public void mouseEntered( MouseEvent e ) { ; }
    public void mouseExited( MouseEvent e ) { ;}
    public void mouseClicked( MouseEvent e ) { ; }

    public void mouseDragged( MouseEvent e) { ; } 
    public void mouseMoved( MouseEvent e ) { ; }

    void DrawObject(){
        int ix, iy;
	float iz, iw;
        int w, h;
        w = raster.getWidth();
        h = raster.getHeight();
	
        raster.fill(getBackground());

        for (int i = 0; i < axesVtcs; i++) {
	    if( tranAxesList[i].getCoord(3) !=0){
		ix = (int) (tranAxesList[i].getCoord(0) / tranAxesList[i].getCoord(3));
		iy = (int) (tranAxesList[i].getCoord(1) / tranAxesList[i].getCoord(3));
		iz = tranAxesList[i].getCoord(2) /tranAxesList[i].getCoord(3) ;
		iw = tranAxesList[i].getCoord(3) /tranAxesList[i].getCoord(3) ;
		if (ix >= 0 && iy >= 0 && ix < w && iy < h){
		    raster.setPixel(axesList[i].getColor(), ix, iy);
		}
	    }
        }

	int nRendered = 0;
        for (int i = 0; i < vertices; i++) {
	    if( tranList[i].getCoord(3) !=0){
		ix = (int) (tranList[i].getCoord(0) / tranList[i].getCoord(3));
		iy = (int) (tranList[i].getCoord(1) / tranList[i].getCoord(3));
		iz = tranList[i].getCoord(2) /tranList[i].getCoord(3) ;
		iw = tranList[i].getCoord(3) /tranList[i].getCoord(3) ;
		if (ix >= 0 && iy >= 0 && ix < w && iy < h){
		    nRendered++;
		    raster.setPixel(vertList[i].getColor(), ix, iy);
		}
	    }
        }
	showStatus("Vertices rendered = " + nRendered ); 
	showStatus("PercentNOT rendered = " + (vertices-nRendered)*100/vertices ); 
    }
    
}
