/**
 * BoxApplet.java
 *
 * Alex S.
 */

import java.util.*;
import java.awt.*;

public class BoxApplet extends PixApplet {

    /**
     * the renderer
     */
    Render r;

    pMatrix matrix = new pMatrix();

    // angle, mouse state
    private float m_anglex,m_angley;
    private int m_mousex,m_mousey;

    public float m_angledx = (float)Math.PI/156;
    public float m_angledy = (float)Math.PI/128;

    public boolean dragged = false;

    float[] vect = new float[4];
    float[] norm = new float[4];

    Vector lights = new Vector();

    public Shape shape;

    public Shape sphere = new Sphere(
        new Material(
            (float)0.2,(float)0.2,(float)0.7,    // ambient
            (float)0.4,(float)0.4,(float)1,      // diffuse
            1,1,1,      // specular
            (float)2       // the higher, the more concentrated
        )       
    );

    public Shape cube = new Cube(
        new Material(
            (float)0.1,(float)0.5,(float)0.5,      // ambient
            (float)0.1,(float)0.5,(float)0.5,      // diffuse
            (float)0.1,(float)0.5,(float)0.2,      // specular
            (float)0.2       // the higher, the more concentrated
        )       
    );

    /**
     * initialize
     */
    public void init(){
        super.init();
        r = new Render(W,H,pix);

	m_mousex = m_mousey = 0;
	m_anglex = m_angley = 0;
            
        lights.addElement( new Light( 0, 0,-110, 1,1,1 ) );
        lights.addElement( new Light( 0, 0,-140, 1,1,1 ) );
        //lights.addElement( new Light( 0, 0,-100, 1,1,1 ) );
        fixlights();

        shape = sphere;
    }

    /**
     * ensure all lights sum upto 1.
     */
    public void fixlights(){
        float r,g,b;
        Enumeration enum;
        r=g=b=(float)0.0;
        enum = lights.elements();
        while(enum.hasMoreElements()){
            Light l = (Light)enum.nextElement();
            r += l.r;
            g += l.g;
            b += l.b;
        }
        enum = lights.elements();
        while(enum.hasMoreElements()){
            Light l = (Light)enum.nextElement();
            l.r /= r;
            l.g /= g;
            l.b /= b;
        }
    }

    /**
     * SET PIXELS FOR THIS ANIMATION FRAME
     * (THIS OVERRIDES A METHOD IN PIXAPPLET CLASS)
     */
    public void setPix(int frame) 
    { 
        m_angley += m_angledy;
        m_anglex += m_angledx;

        r.clear();

 	matrix.push();
			
	matrix.translate(0,0,-140);
	matrix.scale(30);
	matrix.rotatey(m_angley);
	matrix.rotatex(m_anglex);

        matrix.translate(0,0,(float)-1.4);

        // dump objects
        shape = cube;
        dumpObject();
        
        matrix.translate(0,0,(float)+2.8);
        
        shape = sphere;
        dumpObject();
        
        
	matrix.pop();

        /*
        for(int i=0;i<100;i++){

            float ax = Math.floor(Math.random()*W);
            float ay = Math.floor(Math.random()*H);
            float az = 100;
            float ar = Math.random();
            float ag = Math.random();
            float ab = Math.random();

            float bx = Math.floor(Math.random()*W);
            float by = Math.floor(Math.random()*H);
            float bz = 100;
            float br = Math.random();
            float bg = Math.random();
            float bb = Math.random();

            float cx = Math.floor(Math.random()*W);
            float cy = Math.floor(Math.random()*H);
            float cz = 100;
            float cr = Math.random();
            float cg = Math.random();
            float cb = Math.random();

            //r.clear();
            r.triangle(  
                new float[] {cx,cy,cz,cr,cg,cb},                
                new float[] {ax,ay,az,ar,ag,ab},
                new float[] {bx,by,bz,br,bg,bb}
            );
        }
        */
        
    }

    public void dumpObject()
    {
        matrix.computeInvTrans();
        float focallength = W/2;
            
        r.setshape(shape.faces,shape.verticest);
        float[] v;
        for(int i=0;i<shape.vertices.length;i++){
            vect[0] = shape.vertices[i][0]; 
            vect[1] = shape.vertices[i][1]; 
            vect[2] = shape.vertices[i][2]; 
            vect[3] = 1;
                
            norm[0] = shape.vertices[i][3]; 
            norm[1] = shape.vertices[i][4]; 
            norm[2] = shape.vertices[i][5]; 
            norm[3] = 0;
                
            vect = matrix.mult(vect);
            norm = matrix.multinvtrans(norm);
                
            float r,g,b;
            r = shape.m.ar;
            g = shape.m.ag;
            b = shape.m.ab;

            Enumeration enum = lights.elements();
            while(enum.hasMoreElements()){
                Light l = (Light)enum.nextElement();
                float[] Li = {
                    l.x-vect[0], 
                    l.y-vect[1], 
                    l.z-vect[2]
                };
                vectorNormalize(Li,Li);
                float Lidotn = dotProduct(Li,norm);
                float[] Hi = {
                    2 * Lidotn * norm[0] - Li[0],    
                    2 * Lidotn * norm[1] - Li[1],    
                    2 * Lidotn * norm[2] - Li[2],    
                };
                vectorNormalize(Hi,Hi);
                float[] Ei = {
                    0 - vect[0],
                    0 - vect[1],
                    0 - vect[2]
                };
                vectorNormalize(Ei,Ei);
                float Hidote = dotProduct(Hi,Ei);
                float p = (float)Math.pow(Math.max(0,Hidote),shape.m.p);
                    
                r += l.r * (shape.m.dr*Math.max(0,Lidotn)+shape.m.sr*p);
                g += l.g * (shape.m.dg*Math.max(0,Lidotn)+shape.m.sg*p);
                b += l.b * (shape.m.db*Math.max(0,Lidotn)+shape.m.sb*p);
            }
                
            shape.verticest[i][0] = 
                W/2 + (float)Math.floor(focallength*vect[0]/vect[2]);
            shape.verticest[i][1] = 
                H/2 + (float)Math.floor(focallength*vect[1]/vect[2]);
            shape.verticest[i][2] = focallength/vect[2];
            shape.verticest[i][3] = r; 
            shape.verticest[i][4] = g;
            shape.verticest[i][5] = b;

                /*
                String s = "";
                for(int q=0;q<6;q++){                    
                    s += "["+shape.verticest[i][q]+"]";
                }
                s += "";
                System.out.println(s);
                */

        }
        r.rendershape();
    }

    /**
     * normalize
     */
    float vectorNormalize(float[] in, float[] out ) {
        float	length, ilength;
        length = (float)Math.sqrt(in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
        if (length == 0){
            return 0;
        }
        ilength = (float)(1.0/length);
        out[0] = in[0]*ilength;
        out[1] = in[1]*ilength;
        out[2] = in[2]*ilength;
        return length;
    }	
	
    /**
     * dot product
     */
    float dotProduct (float[] v1, float[] v2){
        return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
    }    
        
    /**
     * mouse event handler: let the user move things
     */
    public boolean mouseDown(Event evt, int x, int y)
    {
        dragged = false;
        m_angledx = 0;
        m_angledy = 0;
        return true;
    }
    
    public boolean mouseUp(Event evt, int x, int y)
    {
        dragged = false;
        m_angledx = (float)(Math.PI/156);
        m_angledy = (float)(Math.PI/128);
        return true;
    }
	
    /**
     * mouse event handler: let the user move things
     */
    public boolean mouseDrag(Event evt, int x, int y)
    {
        dragged = true;
        if((m_mousey - y) < 0)
            m_anglex += (float)(Math.PI/45);
        if((m_mousey - y) > 0)
            m_anglex -= (float)(Math.PI/45);
        if((m_mousex - x) < 0)
            m_angley -= (float)(Math.PI/45);
        if((m_mousex - x) > 0)
            m_angley += (float)(Math.PI/45);
        m_mousex = x;
        m_mousey = y;
        damage = true;
        return true;
    }	

}


