/*
 * Render.java
 *
 * Alex S.
 */

public class Render {

    public int width,height;
    public int[] pix;
    public float[] pixz;

    public int[][] faces;
    public float[][] vertices;

    public int[] texture;
    public int texture_width,texture_height;
    public boolean enabletexture;

    public final static int FP = 11;
    public final static int FPval = 1 << FP;

    public int[] rgb = {0,0,0};

    public Render(int w,int h,int[] pix){
        width = w;
        height = h;
        this.pix = pix;
        pixz = new float[pix.length];
    }
    
    /**
     * get a pixel from texture
     */
    public int gett(float i,float j){
        //System.out.println("getting i: "+i+", j: "+j);
        int x = (int)(j * texture_width) % texture_width;
        int y = (int)(i * texture_height) % texture_height;
        //System.out.println("getting x: "+x+", y: "+y);
        return texture[y * texture_width + x];
    }
    
    /**
     * clear both screen and depth buffer
     */
    public void clear(){
        for(int i=0;i<pix.length;i++){
            pixz[i] = (float)0.0;
            pix[i] = PixApplet.pack(0x66,0x66,0xAA);
        }
    }

    /**
     * set current faces/vertices
     */
    public void setshape(int[][] faces,float[][] vertices){
        this.faces = faces;
        this.vertices = vertices;
    }

    /**
     * setup texture
     */
    public void settexture(Texture t){
        texture = t.map;
        texture_width = t.width;
        texture_height = t.height;
        enabletexture = true;            
    }

    public void enabletexture(){
        enabletexture = true;
    }
    public void disabletexture(){
        enabletexture = false;
    }
    

    /**
     * render shape (all faces)
     * (assume shape has been transformed, and color info
     * has been computed).
     */
    public void rendershape(){
        for(int i=0;i<faces.length;i++){
            renderface(i);
        }
    }

    /**
     * render face
     * (assumes all vertices have been properly setup)
     */
    public void renderface(int n){
        int[] face = faces[n];
        int i,j = face.length-1;
        
        if(enabletexture){
            for(i=1;i<j;i++){
                trianglet(
                    vertices[face[0]],
                    vertices[face[i]],
                    vertices[face[i+1]]
                );
            }        
        }else{
            for(i=1;i<j;i++){
                triangle(
                    vertices[face[0]],
                    vertices[face[i]],
                    vertices[face[i+1]]
                );
            }        
        }
    }

    /**
     * render trinagle
     */
    public void triangle(float[] a,float[] b,float[] c)
    {
        // sort 3 points by Y
        float[] tmp;
        if(a[1] < b[1]){
            tmp = a;
            a = b;
            b = tmp;
        }
        if(a[1] < c[1]){
            tmp = a;
            a = c;
            c = tmp;
        }
        if(b[1] < c[1]){
            tmp = b;
            b = c;
            c = tmp;
        }
        // check special cases
        if(c[1] == b[1]){
            if(c[0] < b[0]){
                trapezoid(c,b,a,a);
            }else{
                trapezoid(b,c,a,a);
            }
        }else if(b[1] == a[1]){
            if(b[0] < a[0]){
                trapezoid(c,c,b,a);
            }else{
                trapezoid(c,c,a,b);
            }
        }else{
            float t = (a[1]-b[1])/(a[1]-c[1]);
            float d[] = {
                a[0]+t*(c[0]-a[0]),
                b[1],
                a[2]+t*(c[2]-a[2]),
                a[3]+t*(c[3]-a[3]),
                a[4]+t*(c[4]-a[4]),
                a[5]+t*(c[5]-a[5]),
            };
            /*
            String s = "";
            for(int i=0;i<6;i++){
                s += "a["+i+"]:"+a[i]+"; ";
                s += "b["+i+"]:"+b[i]+"; ";
                s += "c["+i+"]:"+c[i]+"; ";
                s += "d["+i+"]:"+d[i]+"; ";
            }
            System.out.println("chopping case: t:"+t+"; "+s);
            */
            if(d[0] < b[0]){
                trapezoid(c,c,d,b);
                trapezoid(d,b,a,a);
            }else{
                trapezoid(c,c,b,d);
                trapezoid(b,d,a,a);
            }
        }
    }

    /**
     * render triagle (texture mapped)
     */
    public void trianglet(float[] a,float[] b,float[] c)
    {
        // sort 3 points by Y
        float[] tmp;
        if(a[1] < b[1]){
            tmp = a;
            a = b;
            b = tmp;
        }
        if(a[1] < c[1]){
            tmp = a;
            a = c;
            c = tmp;
        }
        if(b[1] < c[1]){
            tmp = b;
            b = c;
            c = tmp;
        }
        // check special cases
        if(c[1] == b[1]){
            if(c[0] < b[0]){
                trapezoidti(c,b,a,a);
            }else{
                trapezoidti(b,c,a,a);
            }
        }else if(b[1] == a[1]){
            if(b[0] < a[0]){
                trapezoidti(c,c,b,a);
            }else{
                trapezoidti(c,c,a,b);
            }
        }else{
            float t = (a[1]-b[1])/(a[1]-c[1]);
            float d[] = {
                a[0]+t*(c[0]-a[0]),
                b[1],
                a[2]+t*(c[2]-a[2]),
                a[3]+t*(c[3]-a[3]),
                a[4]+t*(c[4]-a[4]),
                a[5]+t*(c[5]-a[5]),
                a[6]+t*(c[6]-a[6]),
                a[7]+t*(c[7]-a[7]),
            };
            /*
            String s = "";
            for(int i=0;i<6;i++){
                s += "a["+i+"]:"+a[i]+"; ";
                s += "b["+i+"]:"+b[i]+"; ";
                s += "c["+i+"]:"+c[i]+"; ";
                s += "d["+i+"]:"+d[i]+"; ";
            }
            System.out.println("chopping case: t:"+t+"; "+s);
            */
            if(d[0] < b[0]){
                trapezoidti(c,c,d,b);
                trapezoidti(d,b,a,a);
            }else{
                trapezoidti(c,c,b,d);
                trapezoidti(b,d,a,a);
            }
        }
    }


    
    /**
     * render a trapezoid 
     *
     * a b
     * c d
     */
    public void trapezoid(float[] a,float[] b,float[] c,float[] d)
    {
        float ax = a[0];
        float ay = a[1];
        float az = a[2];
        float ar = a[3];
        float ag = a[4];
        float ab = a[5];

        float bx = b[0];
        float by = b[1];
        float bz = b[2];
        float br = b[3];
        float bg = b[4];
        float bb = b[5];

        float cx = c[0];
        float cy = c[1];
        float cz = c[2];
        float cr = c[3];
        float cg = c[4];
        float cb = c[5];

        float dx = d[0];
        float dy = d[1];
        float dz = d[2];
        float dr = d[3];
        float dg = d[4];
        float db = d[5];

        float ydiff = cy - ay;

        float axt = (cx - ax) / ydiff;
        float azt = (cz - az) / ydiff;
        float art = (cr - ar) / ydiff;
        float agt = (cg - ag) / ydiff;
        float abt = (cb - ab) / ydiff;

        float bxt = (dx - bx) / ydiff;
        float bzt = (dz - bz) / ydiff;
        float brt = (dr - br) / ydiff;
        float bgt = (dg - bg) / ydiff;
        float bbt = (db - bb) / ydiff;

        float x,y,z,r,g,bcolor,
            tz,tr,tg,tb,xdiff;
        
        float w;

        //System.out.println("ay:"+ay+"; cy:"+cy);

        if(ay < 0)
            ay = 0;
        if(cy >= height)
            cy = height-1;

        while(ay <= cy){

            w = width * ay;

            xdiff = bx - ax;

            if(xdiff > 0){
                x = ax;
                z = az;
                r = ar;
                g = ag;
                bcolor = ab;

                tz = (bz - az) / xdiff;
                tr = (br - ar) / xdiff;
                tg = (bg - ag) / xdiff;
                tb = (bb - ab) / xdiff;

                while(x < bx){
                    
                    if(pixz[(int)(w + x)] > z){
                        pix[(int)(w + x)] = PixApplet.pack(
                            (int)(0xFF*r),
                            (int)(0xFF*g),
                            (int)(0xFF*bcolor)
                        );
                        pixz[(int)(w + x)] = z;
                    }

                    x += 1;
                    z += tz;
                    r += tr;
                    g += tg;
                    bcolor += tb;
                }
            }else{
                if(pixz[(int)(w + ax)] > az){                 
                    pix[(int)(w + ax)] = PixApplet.pack(
                        (int)(0xFF*ar),
                        (int)(0xFF*ag),
                        (int)(0xFF*ab)
                    );
                    pixz[(int)(w + ax)] = az;
                }
            }

            ay += 1;
            //by += 1;
            
            ax += axt;
            az += azt;
            ar += art;
            ag += agt;
            ab += abt;
            
            bx += bxt;
            bz += bzt;
            br += brt;
            bg += bgt;
            bb += bbt;
        }

    }

    /**
     * render a trapezoid (texture mapped) 
     *
     * a b
     * c d
     */
    public void trapezoidt(float[] a,float[] b,float[] c,float[] d)
    {
        float ax = a[0];
        float ay = a[1];
        float az = a[2];
        float ar = a[3];
        float ag = a[4];
        float ab = a[5];
        float ai = a[6];
        float aj = a[7];

        float bx = b[0];
        float by = b[1];
        float bz = b[2];
        float br = b[3];
        float bg = b[4];
        float bb = b[5];
        float bi = b[6];
        float bj = b[7];

        float cx = c[0];
        float cy = c[1];
        float cz = c[2];
        float cr = c[3];
        float cg = c[4];
        float cb = c[5];
        float ci = c[6];
        float cj = c[7];

        float dx = d[0];
        float dy = d[1];
        float dz = d[2];
        float dr = d[3];
        float dg = d[4];
        float db = d[5];
        float di = d[6];
        float dj = d[7];

        float ydiff = cy - ay;

        float axt = (cx - ax) / ydiff;
        float azt = (cz - az) / ydiff;
        float art = (cr - ar) / ydiff;
        float agt = (cg - ag) / ydiff;
        float abt = (cb - ab) / ydiff;
        float ait = (ci - ai) / ydiff;
        float ajt = (cj - aj) / ydiff;

        float bxt = (dx - bx) / ydiff;
        float bzt = (dz - bz) / ydiff;
        float brt = (dr - br) / ydiff;
        float bgt = (dg - bg) / ydiff;
        float bbt = (db - bb) / ydiff;
        float bit = (di - bi) / ydiff;
        float bjt = (dj - bj) / ydiff;

        float x,y,z,r,g,i,j,bcolor,
            tz,tr,tg,tb,ti,tj,xdiff;
        
        float w;

        //System.out.println("ay:"+ay+"; cy:"+cy);

        if(ay < 0)
            ay = 0;
        if(cy >= height)
            cy = height-1;

        while(ay <= cy){

            w = width * ay;

            xdiff = bx - ax;

            if(xdiff > 0){
                x = ax;
                z = az;
                r = ar;
                g = ag;
                i = ai;
                j = aj;
                bcolor = ab;

                tz = (bz - az) / xdiff;
                tr = (br - ar) / xdiff;
                tg = (bg - ag) / xdiff;
                tb = (bb - ab) / xdiff;
                ti = (bi - ai) / xdiff;
                tj = (bj - aj) / xdiff;

                while(x < bx){
                    
                    if(pixz[(int)(w + x)] > z){
                        int pixel = texture[(int)i * texture_width + (int)j];
                        pix[(int)(w + x)] =                         
                        PixApplet.pack(
                            (int)((0xFF & pixel) + r*0xFF),
                            (int)(((0xFF00 & pixel) >> 8) + g*0xFF),
                            (int)(((0xFF0000 & pixel) >> 16) + bcolor*0xFF)
                        );
                        
                        pixz[(int)(w + x)] = z;
                    }

                    x += 1;
                    z += tz;
                    r += tr;
                    g += tg;
                    i += ti;
                    j += tj;
                    bcolor += tb;
                }
            }else{
                if(pixz[(int)(w + ax)] > az){
                    
                    int pixel = texture[(int)ai * texture_width + (int)aj];
                    pix[(int)(w + ax)] =                         
                    PixApplet.pack(
                            (int)((0xFF & pixel) ),
                            (int)(((0xFF00 & pixel) >> 8) ),
                            (int)(((0xFF0000 & pixel) >> 16) )
                        );
                    /*
                    pix[(int)(w + ax)] = gett(ai,aj);                    
                    PixApplet.pack(
                        (int)(0xFF*ar),
                        (int)(0xFF*ag),
                        (int)(0xFF*ab)
                    );
                    */
                    pixz[(int)(w + ax)] = az;
                }
            }

            ay += 1;
            //by += 1;
            
            ax += axt;
            az += azt;
            ar += art;
            ag += agt;
            ab += abt;
            ai += ait;
            aj += ajt;
            
            bx += bxt;
            bz += bzt;
            br += brt;
            bg += bgt;
            bb += bbt;
            bi += bit;
            bj += bjt;

            //System.out.println("ai: "+ai+"; aj: "+aj+"; bi: "+bi+"; bj: "+bj);
            
        }

    }



    /**
     * render a trapezoid (texture mapped) (using int math)
     *
     * a b
     * c d
     */
    public void trapezoidti(float[] a,float[] b,float[] c,float[] d)
    {
        int ax = (int)(a[0] * FPval);
        int ay = (int)(a[1] * FPval);
        int az = (int)(a[2] * FPval);
        int ar = (int)(a[3] * FPval);
        int ag = (int)(a[4] * FPval);
        int ab = (int)(a[5] * FPval);
        int ai = (int)(a[6] * FPval);
        int aj = (int)(a[7] * FPval);

        int bx = (int)(b[0] * FPval);
        int by = (int)(b[1] * FPval);
        int bz = (int)(b[2] * FPval);
        int br = (int)(b[3] * FPval);
        int bg = (int)(b[4] * FPval);
        int bb = (int)(b[5] * FPval);
        int bi = (int)(b[6] * FPval);
        int bj = (int)(b[7] * FPval);

        int cx = (int)(c[0] * FPval);
        int cy = (int)(c[1] * FPval);
        int cz = (int)(c[2] * FPval);
        int cr = (int)(c[3] * FPval);
        int cg = (int)(c[4] * FPval);
        int cb = (int)(c[5] * FPval);
        int ci = (int)(c[6] * FPval);
        int cj = (int)(c[7] * FPval);

        int dx = (int)(d[0] * FPval);
        int dy = (int)(d[1] * FPval);
        int dz = (int)(d[2] * FPval);
        int dr = (int)(d[3] * FPval);
        int dg = (int)(d[4] * FPval);
        int db = (int)(d[5] * FPval);
        int di = (int)(d[6] * FPval);
        int dj = (int)(d[7] * FPval);

        int ydiff = (cy - ay) | 1;

        int axt = ((cx - ax)<<FP) / ydiff;
        int azt = ((cz - az)<<FP) / ydiff;
        int art = ((cr - ar)<<FP) / ydiff;
        int agt = ((cg - ag)<<FP) / ydiff;
        int abt = ((cb - ab)<<FP) / ydiff;
        int ait = ((ci - ai)<<FP) / ydiff;
        int ajt = ((cj - aj)<<FP) / ydiff;

        int bxt = ((dx - bx)<<FP) / ydiff;
        int bzt = ((dz - bz)<<FP) / ydiff;
        int brt = ((dr - br)<<FP) / ydiff;
        int bgt = ((dg - bg)<<FP) / ydiff;
        int bbt = ((db - bb)<<FP) / ydiff;
        int bit = ((di - bi)<<FP) / ydiff;
        int bjt = ((dj - bj)<<FP) / ydiff;

        int x,y,z,r,g,i,j,bcolor,
            tz,tr,tg,tb,ti,tj,xdiff;
        
        int w;

        //System.out.println("ay:"+ay+"; cy:"+cy);

        if(ay < 0)
            ay = 0;
        if((cy>>FP) >= height)
            cy = (height-1)<<FP;

        while(ay <= cy){

            w = (width * ay) >> FP;

            xdiff = (bx - ax) | 1;

            if(xdiff > 0){
                x = ax>>FP;
                z = az;
                r = ar;
                g = ag;
                i = ai;
                j = aj;
                bcolor = ab;

                //System.out.println("y:"+(w>>FP)+"; ij: "+(i>>FP)+", "+(j>>FP));

                tz = ((bz - az)<<FP) / xdiff;
                tr = ((br - ar)<<FP) / xdiff;
                tg = ((bg - ag)<<FP) / xdiff;
                tb = ((bb - ab)<<FP) / xdiff;
                ti = ((bi - ai)<<FP) / xdiff;
                tj = ((bj - aj)<<FP) / xdiff;

                while(x < (bx>>FP)){
                    
                    if(pixz[w + x] > (z>>FP)){
                        int pixel = texture[(i>>FP) * 
                            texture_width + (j>>FP)];
                        PixApplet.unpack(rgb,pixel);
                        rgb[0] += ((r*0xFF)>>FP);
                        rgb[1] += ((g*0xFF)>>FP);
                        rgb[2] += ((bcolor*0xFF)>>FP);
                        pix[w + x] = PixApplet.pack(rgb[0],rgb[1],rgb[2]);
                        pixz[w + x] = z>>FP;
                    }

                    x += 1;
                    z += tz;
                    r += tr;
                    g += tg;
                    i += ti;
                    j += tj;
                    bcolor += tb;
                }
            }else{
                if(pixz[w + (ax>>FP)] > (az>>FP)){                
                    pix[w + (ax>>FP)]  = texture[(ai>>FP) * 
                            texture_width + (aj>>FP)];
                    pixz[w + (ax>>FP)] = az>>FP;
                }
            }

            ay += FPval;
            //by += 1;
            
            ax += axt;
            az += azt;
            ar += art;
            ag += agt;
            ab += abt;
            ai += ait;
            aj += ajt;
            
            bx += bxt;
            bz += bzt;
            br += brt;
            bg += bgt;
            bb += bbt;
            bi += bit;
            bj += bjt;

            //System.out.println("ai: "+ai+"; aj: "+aj+"; bi: "+bi+"; bj: "+bj);
            
        }

    }
    


    
    
    /**
     * draw a line of a particular color
     */
    public void line(int x1,int y1,int x2,int y2,int color)
    {
        int xunit=1,yunit=1,xdiff,ydiff,length,error=0;
        int X1=x1,Y1=y1;
        if ((xdiff=x2-x1) < 0) {
            xdiff=-xdiff;
            xunit=-1;
        }
        if ((ydiff=y2-y1) < 0) {
            ydiff=-ydiff;
            yunit=-1;
        }
        if (ydiff > xdiff) {
            for (length=ydiff;length > 0;length--) {
                //if ((X1 > 0) && (X1 < width) && (Y1 > 0) && (Y1 < height))
                    pix[Y1*width+X1] = color;

                Y1+=yunit;
                error+=xdiff;
                if (error > ydiff) {
                    X1+=xunit;
                    error-=ydiff;
                }
            }
        } else {
            for (length=xdiff;length > 0;length--) {
                //if ((X1 > 0) && (X1 < width) && (Y1 > 0) && (Y1 < height))
                    pix[Y1*width+X1] = color;
                X1+=xunit;
                error+=ydiff;
                if (error > xdiff) {
                    Y1+=yunit;
                    error-=xdiff;
                }
            }
        }
    }
}


