import Drawable;
import Vertex2D;
import TriangleDrawer;
import ColorDrawer;
import java.awt.Color;

class Triangle implements Drawable {

    protected Vertex2D v[];
    protected EdgeEqn  edge[];
    protected double   scale;
    protected double   area;
    protected int      xMin, xMax, yMin, yMax;
    private static byte sort[][] = {
	{0, 1}, {1, 2}, {0, 2}, {2, 0}, {2, 1}, {1, 0}
    };

    protected float[] alpha, red, green, blue;

    public    boolean  flat;

    protected static TriangleDrawer drawer = new ColorDrawer();

    public Triangle() {
	flat = false;
    }

    public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) {

	// Fill in vertex array.
        v = new Vertex2D[3];
        v[0] = v0;
        v[1] = v1;
        v[2] = v2;

	flat = ((v0.argb == v1.argb) && (v1.argb == v2.argb));
	scale = -1;

	// compute edge equations for interpolating colors.
	triangleSetup();
    }


    public static void setTriangleDrawer(TriangleDrawer triDrawer) {
	drawer = triDrawer;
    }
    
    public void Draw(Raster r) {
	drawer.Draw(r, this);
    }

    public String toString() {
	return "[Triange: " + v[0] + ",\n" +
	       "          " + v[1] + ",\n" +
	       "          " + v[2] + "]\n";
    }
	

    /**
     * These next two methods are taken directly from McMillan's lecture
     * notes, with some unneccessary code removed and integer arithmetic
     * replaced with floating-point arithmetic
     */
    protected boolean triangleSetup() {

	if (edge == null) edge = new EdgeEqn[3];
	
	/*  Compute the three edge equations  */
	edge[0] = new EdgeEqn(v[0], v[1]);
	edge[1] = new EdgeEqn(v[1], v[2]);
	edge[2] = new EdgeEqn(v[2], v[0]);
	
	area = edge[0].C + edge[1].C + edge[2].C;
	if (area == 0.0) return false;
                      
	/*  Trick #2: compute bounding box  */
	int xflag = edge[0].flag + 2*edge[1].flag + 4*edge[2].flag;
	int yflag = (xflag >> 3) - 1;
	xflag = (xflag & 7) - 1;
	xMin = (int) (v[sort[xflag][0]].x);
	xMax = (int) (v[sort[xflag][1]].x + 1);
	yMin = (int) (v[sort[yflag][1]].y);
	yMax = (int) (v[sort[yflag][0]].y + 1);
                      
	alpha = new float[3];
	red = new float[3];
	green = new float[3];
	blue = new float[3];
	    
	int t0 = v[0].argb;
	int t1 = v[1].argb;
	int t2 = v[2].argb;
	planeEqn(blue, t0 & 255, t1 & 255, t2 & 255);
	t0 >>= 8;   t1 >>= 8;   t2 >>= 8;
	planeEqn(green, t0 & 255, t1 & 255, t2 & 255);
	t0 >>= 8;   t1 >>= 8;   t2 >>= 8;
	planeEqn(red, t0 & 255, t1 & 255, t2 & 255);
	t0 >>= 8;   t1 >>= 8;   t2 >>= 8;
	planeEqn(alpha, t0 & 255, t1 & 255, t2 & 255);

	return true;
    }


    public void planeEqn(float eqn[], int p0, int p1, int p2)
    {
        float Ap, Bp, Cp;
	if (scale <= 0) {
	    scale = (1 << EdgeEqn.FRACBITS) / ((double) area);
	}
	double sp0 = scale * p0;
	double sp1 = scale * p1;
	double sp2 = scale * p2;
	Ap = (float)(edge[0].A*sp2 + edge[1].A*sp0 + edge[2].A*sp1);
	Bp = (float)(edge[0].B*sp2 + edge[1].B*sp0 + edge[2].B*sp1);
	Cp = (float)(edge[0].C*sp2 + edge[1].C*sp0 + edge[2].C*sp1);
	eqn[0] = Ap;
	eqn[1] = Bp;
	eqn[2] = Cp;
    }

}
