import java.awt.*;
import java.lang.Math;
import java.util.Random;

public class ThinkingMachine extends MISApplet {
    double tlast_ = 0.,tend_ = 0.;
    static Random rnd_ = new Random();
	StringBuffer strbuf_ = new StringBuffer(458);
    Vector3D mouse_ = new Vector3D(),
        tempV_ = new Vector3D(),
        tempV2_ = new Vector3D();
    boolean pressing_ = false;
    Matrix3D view_ = new Matrix3D(),
        perspectivator_ = new Matrix3D(),
        screen_ = new Matrix3D(),
        perspectiviewport_ = new Matrix3D(),
        tempM_ = new Matrix3D(),
        tempM2_ = new Matrix3D();
	Colorizer colorizer_ = new Colorizer();
    Triangles tris_ = new Triangles(20000);
    ClippyTriangles cliptris_ = new ClippyTriangles(20000);
    SplittyTriangles splitris_ = new SplittyTriangles(20000);
    final double F = -10;
    Shape all_[] = new Shape[10];
    int zbuf_[],pixbuf_[];
    Vector3D[] posses = new Vector3D[10],
        vels = new Vector3D[10],
        rots = new Vector3D[10];
    double BND = 4;
    

    public void initialize() {
        super.initialize();

        view_.translation(0,0,F);
        perspectivator_.perspectivator(F);
        
        int smdim = Math.min(W,H);
        screen_.identity();
        screen_.scale(smdim/2.,-smdim/2.,smdim/2);  // does z matter?
        screen_.translate(W/2.,H/2.,0);
        
        zbuf_ = new int[W*H];
		pixbuf_ = new int[W*H];
                
        for(int i=0; i<all_.length; ++i) {
            all_[i] = new Zero(21);
			double r = Math.max(0.01,rnd_.nextGaussian()+2);
            all_[i].setScale(r,r,.25);
            posses[i] = new Vector3D(rnd_.nextGaussian()*2,rnd_.nextGaussian()*2,rnd_.nextGaussian()*2);
            vels[i] = new Vector3D(rnd_.nextGaussian()/12,rnd_.nextGaussian()/12,rnd_.nextGaussian()/12);
            rots[i] = new Vector3D(rnd_.nextGaussian()/30,rnd_.nextGaussian()/30,rnd_.nextGaussian()/30);
            all_[i].setTranslation(posses[i]);
        }
    }
    public void doFrame(double t, int array[]) {
        double dt = t - tlast_,prt = t,prt2;  
		boolean stat=false,breakdown=false,copyout=true;
		int dest[] = copyout?pixbuf_:array; // is it faster not to write directly to array?
		tlast_ = t;  
		
		colorizer_.sources_[0].pt.set(0,Math.cos(t/5));
		colorizer_.sources_[0].pt.set(1,0);
		colorizer_.sources_[0].pt.set(2,Math.sin(t/5));
		colorizer_.sources_[0].pt.normalize();
		if(stat) {
			strbuf_.delete(0,strbuf_.length());
			strbuf_.append("dt: "+dt+" fps: "+(1./dt)+" outside: "+(t-tend_)+"\n");
		}
		if(breakdown)
			{strbuf_.append("rilly "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
		java.util.Arrays.fill(dest,255<<24);
		java.util.Arrays.fill(zbuf_,0);
		if(breakdown)
	 		{strbuf_.append("clear "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
		cliptris_.reset();
        splitris_.reset();
        tris_.reset();
		if(breakdown)
	 		{strbuf_.append("reset "+((prt2=currTime())-prt)+"s "); prt = prt2;}		
			
		for(int i = 0; i<all_.length; ++i) {
			Shape sh = all_[i];
            if(sh!=null) {
                if(posses[i].x()<-BND)
                    vels[i].set(0,Math.abs(vels[i].x()));
                if(posses[i].x()>BND)
                    vels[i].set(0,-Math.abs(vels[i].x()));
                if(posses[i].y()<-BND)
                    vels[i].set(1,Math.abs(vels[i].y()));
                if(posses[i].y()>BND)
                    vels[i].set(1,-Math.abs(vels[i].y()));
                if(posses[i].z()<-BND)
                    vels[i].set(2,Math.abs(vels[i].z()));
                if(posses[i].z()>BND)
                    vels[i].set(2,-Math.abs(vels[i].z()));
				vels[i].times(dt,tempV_);
                posses[i].add(tempV_);
                sh.setTranslation(posses[i]);
                rots[i].times(t,tempV_);
                sh.setRotation(tempV_);
			}
		}
		if(breakdown)
			{strbuf_.append("xlate "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
		for(Shape sh : all_)
			if(sh!=null)		
                sh.transform(view_);
		if(breakdown)
			{strbuf_.append("transform "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
		for(Shape sh : all_)
			if(sh!=null)
                tris_.addShape(sh);				
		tris_.hAVEqUALITY();
		if(breakdown)
			{strbuf_.append("triangle "+((prt2=currTime())-prt)+"s "); prt = prt2;}

        cliptris_.clip(tris_);
		cliptris_.hAVEqUALITY();
		if(breakdown)
			{strbuf_.append("clip "+((prt2=currTime())-prt)+"s "); prt = prt2;}

		// this can go anywhere after transform b/c 
		// everything later only lerps v
		cliptris_.colorize(colorizer_); 
		if(breakdown)
			{strbuf_.append("color "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
        perspectivator_.multiply(screen_,perspectiviewport_);
        cliptris_.toScreen(perspectiviewport_);
		if(breakdown)
			{strbuf_.append("perspectiv "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
        splitris_.split(cliptris_);
		if(breakdown)
			{strbuf_.append("split "+((prt2=currTime())-prt)+"s "); prt = prt2;}
					
		splitris_.render(dest,zbuf_,W,H);
		if(breakdown)
			{strbuf_.append("render "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
		if(copyout)
			System.arraycopy(dest,0,array,0,W*H);
		if(breakdown)
			{strbuf_.append("copyout "+((prt2=currTime())-prt)+"s "); prt = prt2;}
			
		if(stat) {
			System.out.println(strbuf_);
			tend_ = currTime();
		}
    }
    public boolean mouseMove(Event e, int wx, int wy) {
        mouse_.set(0,wx);
        mouse_.set(1,wy);
        return true;
    }
    public boolean mouseDown(Event e, int wx, int wy) {
        pressing_ = true;
        return true;
    }
    public boolean mouseUp(Event e, int wx, int wy) {
        pressing_ = false;
        return true;
    }
    public boolean keyDown(Event e, int key) {
        switch(key) {
        case Event.UP:
            break;
        case Event.DOWN:
            break;
        case 'z':
            break;
        case Event.RIGHT:
            break;
        case Event.LEFT:
            break;
        }           
        return true;
    }   
}
