import java.awt.*;
import java.lang.Math;
import java.util.Random;

public class GhostBubbles extends MISApplet {
    double tlast_ = 0.;
    static Random rnd_ = new Random();
    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();
    Triangles tris_ = new Triangles(20000);
    ClippyTriangles cliptris_ = new ClippyTriangles(20000);
    SplittyTriangles splitris_ = new SplittyTriangles(20000);
    Color backg_ = Color.white;
    final double F = -10;
    Shape all_[] = new Shape[10];
    int zbuf_[];
    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];
                
        for(int i=0; i<all_.length; ++i) {
            all_[i] = new Sphere(13,13);
            double r = rnd_.nextGaussian()+2;
            all_[i].setScale(r,r,r);
            posses[i] = new Vector3D(rnd_.nextDouble()*8,rnd_.nextDouble()*8,rnd_.nextDouble()*8);
            vels[i] = new Vector3D(rnd_.nextGaussian()/24,rnd_.nextGaussian()/24,rnd_.nextGaussian()/24);
            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_;    
        //System.out.println();
        //System.out.println("FRAME at t="+t);

        for(int i=0; i<W*H; ++i) {
            array[i] = MISApplet.pack(0,0,0);
            zbuf_[i] = 0;
        }
        cliptris_.reset();
        splitris_.reset();
                
        tris_.reset();
        int i = 0; // estupido
        for(Shape sh : all_) 
            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()));
                posses[i].add(vels[i]);
                sh.setTranslation(posses[i]);
                rots[i].times(t,tempV_);
                sh.setRotation(tempV_);
                ++i;
                sh.transform(view_);
                tris_.addShape(sh);
            }
        cliptris_.clip(tris_);
        perspectivator_.multiply(screen_,perspectiviewport_);
        cliptris_.toScreen(perspectiviewport_);
        splitris_.split(cliptris_);
        
        splitris_.render(array,zbuf_,W,H);
            
        tlast_ = t;
    }
    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;
    }   
}
