import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import Point3D;
import Matrix3D;

public class TestMatrix extends Applet implements Runnable {
	Thread animate;
    final static int CHUNKSIZE = 900;
    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;
    float theta=0;

    public void init( )
    {
        raster = new Raster(size().width, size().height);
        raster.fill(getBackground());
        screen = raster.toImage();

        eyex = 5;        eyey = 3;        eyez = -5;
        lookatx = 0;     lookaty = 0;     lookatz = 0;
        upx = 0;         upy = 1;         upz = 0;
        fov = 25;

        vertList = new Point3D[CHUNKSIZE];
        vertices = 900;
        
        worldList = new Point3D[vertList.length];
        tranList = new Point3D[vertList.length];
        for (int i = 0; i < vertices; i++) {
            vertList[i] = new Point3D(i/900,i/900,i/900);
            worldList[i] = new Point3D();
            tranList[i] = new Point3D();
        }
	newWave();
        
        view = new Matrix3D(raster);
        float t = (float) Math.sin(Math.PI*(fov/2)/180);
        float s = (t*size().height)/size().width;
            
        view.perspective(-t, t, -s, s, -1, -200);
//        view.orthographic(-t,t,-s,s,-1,-200);
        view.lookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz);
        
        model = new Matrix3D();
        long time = System.currentTimeMillis();
        model.transform(vertList, worldList, 0, vertices);
        view.transform(worldList, tranList, 0, vertices);
        DrawObject();
        time = System.currentTimeMillis() - time;
        showStatus("Time = "+time+" ms");
        screen = raster.toImage();
    }

    public void paint(Graphics g)
    {
        g.drawImage(screen, 0, 0, this);
    }

	public void newWave()
	{
		for (int x = 0; x <= 29; ++x)
			for (int y = 0; y <= 29; ++y)
			{
				double nx = x;
				double ny = y;
				vertList[x*30+y].setx((float)nx/25);
				vertList[x*30+y].sety((float)ny/25);
				nx = (nx-15)/5;
				ny = (ny-15)/5;
				vertList[x*30+y].setz((float)(Math.cos(ny*ny+nx*nx-theta)/5));
				vertList[x*30+y].setw(1);
			}
	}
	
	public void updateImage()
	{
		
		newWave();
		model.transform(vertList, worldList, 0, vertices);
           	view.transform(worldList, tranList, 0, vertices);
		DrawObject();
		screen = raster.toImage();
	
		repaint();	
		theta += .5;
		
	}
	
    public void update(Graphics g)
    {
        paint(g);
    }

    float v0x, v0y, v0z;

    public boolean mouseDown(Event e, int x, int y)
    {
        if (e.clickCount > 1) {
            showStatus("Resetting model matrix");
            model.loadIdentity();
            model.transform(vertList, worldList, 0, vertices);
            view.transform(worldList, tranList, 0, vertices);
            DrawObject();
            screen = raster.toImage();
            repaint();
	}

	return true;
	
    }

    public boolean mouseDrag(Event e, int x, int y)
    {
        if (e.metaDown()) {
        } else {
            float v1x = (float) (x - (size().width / 2));
            float v1y = (float) ((size().height / 2) - y);
            float v1z = (float) size().width;
            float l = (float) (1 / Math.sqrt(v1x*v1x + v1y*v1y + v1z*v1z));
            v1x *= l;
            v1y *= l;
            v1z *= l;

            float ax = v0y*v1z - v0z*v1y;
            float ay = v0z*v1x - v0x*v1z;
            float az = v0x*v1y - v0y*v1x;
            l = (float) Math.sqrt(ax*ax + ay*ay + az*az);
            float theta = (float) Math.asin(l);
            if (v0x*v1x + v0y*v1y + v0z*v1z < 0)
                theta += (float) Math.PI / 2;
            model.rotate(ax, ay, az, theta);
            v0x = v1x;
            v0y = v1y;
            v0z = v1z;
            model.transform(vertList, worldList, 0, vertices);
            view.transform(worldList, tranList, 0, vertices);
            DrawObject();
        }
        screen = raster.toImage();
        repaint();
        return true;
    }

    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(0xffa000a0, ix, iy);
        }
    }
    
    public void start()
    {
    	animate = new Thread(this);
    	animate.start();
    }
    
    public void stop()
    {
    	animate = null;
    }

	public void run()
	{
		while (true)
		{
			try
			{
				animate.sleep(100);
			}    
			catch (InterruptedException e)
			{}
			updateImage();
		}	
	}
}
