import Drawable;
import Vertex2D;
import java.lang.*;

class Triangle implements Drawable {
    protected Vertex2D v[];
    protected Vertex2D vY[];

    double Aa; // alpha values for colors at triangle vertices.
    double Ba;
    double Ca;

    double Ar; // red values for colors at triangle vertices.
    double Br;
    double Cr;

    double Ag; // green values for colors at triangle vertices.
    double Bg;
    double Cg;

    double Ab; // blue values for colors at triangle vertices.
    double Bb;
    double Cb;

    float A2;  // Coefficients for color interpolation
    float A1;
    float A3;
    float B2;
    float B1;
    float B3;
    float C2;
    float C1;
    float C3;

    float y0;  // Easier than typing v[0].y, etc.  ;)
    float y1;
    float y2;
    float x0;
    float x1;
    float x2;

    public Triangle() // normal empty constructor for extension.
    {
    }

    public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) // constructor.
    {
        v = new Vertex2D[3];
        v[0] = v0;
        v[1] = v1;
        v[2] = v2;
    }
    
    public void Draw(Raster r) // method for initializing triangle rendering
			      // and calling sub-drawing routines.
    {
	double m;  // m is used as the slope in a line equation mx + b.
	double b;  // b is used as the offset in a line equation mx + b.
	double f;  // arbitrary variable for determining breakpoint side.
	vY = new Vertex2D[3]; // sorted vertices of triangle.
	boolean breakpoint = true; // is there a breakpoint on the triangle?
	boolean breakright = false; // is the breakpoint on the right?


//sort vertices by considering all cases.

// assign the lowest vertice in vY.
	if ((v[0].y < v[1].y) && (v[0].y < v[2].y))
		vY[0] = v[0];
	if ((v[1].y < v[2].y) && (v[1].y < v[0].y))
		vY[0] = v[1];
	if ((v[2].y < v[1].y) && (v[2].y < v[0].y))
		vY[0] = v[2];
	
// assign the middle vertice in vY.
	if ((v[0].y < v[1].y) && (v[0].y > v[2].y))
		vY[1] = v[0];
	if ((v[1].y < v[2].y) && (v[1].y > v[0].y))
		vY[1] = v[1];
	if ((v[2].y < v[1].y) && (v[2].y > v[0].y))
		vY[1] = v[2];

	if ((v[0].y > v[1].y) && (v[0].y < v[2].y))
		vY[1] = v[0];
	if ((v[1].y > v[2].y) && (v[1].y < v[0].y))
		vY[1] = v[1];
	if ((v[2].y > v[1].y) && (v[2].y < v[0].y))
		vY[1] = v[2];

// assign the top vertice in vY.
	if ((v[0].y > v[1].y) && (v[0].y > v[2].y))
		vY[2] = v[0];
	if ((v[1].y > v[2].y) && (v[1].y > v[0].y))
		vY[2] = v[1];
	if ((v[2].y > v[1].y) && (v[2].y > v[0].y))
		vY[2] = v[2];



// in the case that there is no breakpoint, assign the vertices
// in vY by considering all options.

// for v0 = v1;
	if ((int) v[0].y == (int) v[1].y)
		{
		if (v[0].y < v[2].y)
			{
			if (v[0].x < v[1].x)
				{
				vY[0] = v[0];
				vY[1] = v[1];
				vY[2] = v[2];
				}
			else 
				{
				vY[0] = v[1];
				vY[1] = v[0];
				vY[2] = v[2];
				}
			}
		else 
			{
			if (v[0].x < v[1].x)
				{
				vY[0] = v[2];
				vY[1] = v[0];
				vY[2] = v[1];
				}
			else 
				{
				vY[0] = v[2];
				vY[1] = v[1];
				vY[2] = v[0];
				}
			}
		}

// for v1 = v2;
	if ((int) v[1].y == (int) v[2].y)
		{
		if (v[2].y < v[0].y)
			{
			if (v[1].x < v[2].x)
				{
				vY[0] = v[1];
				vY[1] = v[2];
				vY[2] = v[0];
				}
			else 
				{
				vY[0] = v[2];
				vY[1] = v[1];
				vY[2] = v[0];
				}
			}
		else 
			{
			if (v[1].x < v[2].x)
				{
				vY[0] = v[0];
				vY[1] = v[1];
				vY[2] = v[2];
				}
			else 
				{
				vY[0] = v[0];
				vY[1] = v[2];
				vY[2] = v[1];
				}
			}
		}

// for v0 = v2;
	if ((int) v[0].y == (int) v[2].y)
		{
		if (v[1].y < v[2].y)
			{
			if (v[0].x < v[2].x)
				{
				vY[0] = v[1];
				vY[1] = v[0];
				vY[2] = v[2];
				}
			else 
				{
				vY[0] = v[1];
				vY[1] = v[2];
				vY[2] = v[0];
				}
			}
		else 
			{
			if (v[0].x < v[2].x)
				{
				vY[0] = v[0];
				vY[1] = v[2];
				vY[2] = v[1];
				}
			else 
				{
				vY[0] = v[2];
				vY[1] = v[0];
				vY[2] = v[1];
				}
			}
		}   		

	
	//determine breakpoint.
	if ((((int) vY[0].y == (int) vY[1].y) || ((int) vY[0].y == (int) vY[2].y)) || ((int) vY[1].y == (int) vY[2].y))
		breakpoint = false;


// find a point on the line between the highest and lowest points at the
// same height as the breakpoint, and test if the breakpoint is on the
// left or right side of it.  

	m = (vY[0].y - vY[2].y) / (vY[0].x - vY[2].x);

	b = vY[0].y - m * vY[0].x;

	f = vY[1].y;

	f = ((f - b)/m);

	if ((int) vY[0].x == (int) vY[2].x)
		f = vY[0].x;

	//in the case of a breakpoint, determine if it's on the right.
	if ((breakpoint) && (vY[1].x > f))
		breakright = true;


	y0 = v[0].y;  // assign shorter names for convenience.
	y1 = v[1].y;
	y2 = v[2].y;
	x0 = v[0].x;
	x1 = v[1].x;
	x2 = v[2].x;


	// parse and assign color values for the vertices.

	int p0A = (0x000000ff & (v[0].argb >> 24));
	int p0R = (0x000000ff & (v[0].argb >> 16));
	int p0G = (0x000000ff & (v[0].argb >> 8));
	int p0B = (0x000000ff & (v[0].argb));

	int p1A = (0x000000ff & (v[1].argb >> 24));
	int p1R = (0x000000ff & (v[1].argb >> 16));
	int p1G = (0x000000ff & (v[1].argb >> 8));
	int p1B = (0x000000ff & (v[1].argb));

	int p2A = (0x000000ff & (v[2].argb >> 24));
	int p2R = (0x000000ff & (v[2].argb >> 16));
	int p2G = (0x000000ff & (v[2].argb >> 8));
	int p2B = (0x000000ff & (v[2].argb));



	A2 = y1 - y2;  // compute the coefficients of the matrix.
	A3 = y2 - y0;
	A1 = y0 - y1;
	B2 = x2 - x1;
	B3 = x0 - x2;
	B1 = x1 - x0;
	C2 = x1*y2 - x2*y1;
	C3 = x2*y0 - x0*y2;
	C1 = x0*y1 - x1*y0; 


	// compute the determinant, and from it, the area of the triangle.
	double det = x0*(y1 - y2) - x1*(y0-y2) + x2*(y0-y1);
	double Area = 0.5 * det;


	// assign the accumulators.
	Aa = (1/(2*Area))*(A2*p0A + A3*p1A + A1*p2A);
	Ba = (1/(2*Area))*(B2*p0A + B3*p1A + B1*p2A);
	Ca = (1/(2*Area))*(C2*p0A + C3*p1A + C1*p2A);

	Ar = (1/(2*Area))*(A2*p0R + A3*p1R + A1*p2R);
	Br = (1/(2*Area))*(B2*p0R + B3*p1R + B1*p2R);
	Cr = (1/(2*Area))*(C2*p0R + C3*p1R + C1*p2R);

	Ag = (1/(2*Area))*(A2*p0G + A3*p1G + A1*p2G);
	Bg = (1/(2*Area))*(B2*p0G + B3*p1G + B1*p2G);
	Cg = (1/(2*Area))*(C2*p0G + C3*p1G + C1*p2G);

	Ab = (1/(2*Area))*(A2*p0B + A3*p1B + A1*p2B);
	Bb = (1/(2*Area))*(B2*p0B + B3*p1B + B1*p2B);
	Cb = (1/(2*Area))*(C2*p0B + C3*p1B + C1*p2B);


	// test for the four cases of triangles, and call an according
	// method to render that triangle.

	
	if ((breakpoint) && (breakright))
		Tri4(r); // case with a breakpoint on the right.
	if ((breakpoint) && (! breakright))
		Tri3(r); // case with a breakpoint on the left.
	if ((! breakpoint) && ((int) vY[0].y == (int) vY[1].y))
		Tri2(r); // case with no breakpoint, flat on top.
	if ((! breakpoint) && ((int) vY[2].y == (int) vY[1].y))
		Tri1(r); // case with no breakpoint, flat on bottom.
    }

    public void Tri4(Raster r)  // render a triangle with breakpoint on right.
     {
	double mL;
	double mR;
	double bL;
	double bR;
	int i;
	int j;
	int xR;
	int xL;

	// calculate slopes for left and right sides of triangle. 
	mL = (vY[0].y - vY[2].y)/(vY[0].x - vY[2].x);
	bL =  vY[0].y - mL * vY[0].x;

	mR = (vY[0].y - vY[1].y)/(vY[0].x - vY[1].x);
	bR =  vY[0].y - mR * vY[0].x;

	// for the height of the triangle until breakpoint...
	for (i = (int) vY[0].y; i < vY[1].y; i++)
	  {

	// find the leftmost and rightmost points; if the slope is infinite 
	// for this line, assign the points to the bottom of line. 
	    xL = (int) ((i-bL)/mL);
	    if ((int) vY[0].x == (int) vY[2].x)
		xL = (int) vY[2].x;
	    xR = (int) ((i-bR)/mR);
	    if ((int) vY[0].x == (int) vY[1].x)
	    	xR = (int) vY[1].x;
	    
	// for the width of the triangle....
	    for (j = xL; j <= xR; j++)
		{
		// calculate the pixel color and draw it.
		  r.setPixel(PixColor(j,i), j, i);
		}
	  }

	// after breakpoint, reassign the slope for the right side.
	mR = (vY[1].y - vY[2].y)/(vY[1].x - vY[2].x);
	bR =  vY[1].y - mR * vY[1].x;

	// from the breakpoint until the bottom vertice...
	for (i = (int) vY[1].y; i < vY[2].y; i++)
	  {
	//find leftmost and rightmost points; compensate for infinite slope.
	    xL = (int) ((i-bL)/mL);
	    if ((int) vY[0].x == (int) vY[2].x)
		xL = (int) vY[2].x;
	    xR = (int) ((i-bR)/mR);
	    if ((int) vY[1].x == (int) vY[2].x)
	    	xR = (int) vY[2].x;

	//for the width of the triangle....
	    for (j = xL; j <= xR; j++)
		{
		// calculate the pixel color and draw it.
		  r.setPixel(PixColor(j,i), j, i);
		}
	  }

	}

    public void Tri3(Raster r) {
	double mL;
	double mR;
	double bL;
	double bR;
	int i;
	int j;
	int xR;
	int xL;


	// calculate slopes for left and right sides of triangle. 
	mL = (vY[0].y - vY[1].y)/(vY[0].x - vY[1].x);
	bL =  vY[0].y - mL * vY[0].x;

	mR = (vY[0].y - vY[2].y)/(vY[0].x - vY[2].x);
	bR =  vY[0].y - mR * vY[0].x;

	// for the height of the triangle until breakpoint...
	for (i = (int) vY[0].y; i < vY[1].y; i++)
	  {

	// find the leftmost and rightmost points; if the slope is infinite 
	// for this line, assign the points to the bottom of line. 
	    xL = (int) ((i-bL)/mL);
	    if ((int) vY[0].x == (int) vY[1].x)
		xL = (int) vY[1].x;
	    xR = (int) ((i-bR)/mR);
	    if ((int) vY[0].x == (int) vY[2].x)
	    	xR = (int) vY[2].x;
	    
	//for the width of the triangle....
	    for (j = xL; j <= xR; j++)
		{
		// calculate the pixel color and draw it.
		  r.setPixel(PixColor(j,i), j, i);
		}
	  }

	// after breakpoint, reassign the slope for the left side.
	mL = (vY[1].y - vY[2].y)/(vY[1].x - vY[2].x);
	bL =  vY[1].y - mL * vY[1].x;

	// from the breakpoint until the bottom vertice...
	for (i = (int) vY[1].y; i < vY[2].y; i++)
	  {
	//find leftmost and rightmost points; compensate for infinite slope.
	    xL = (int) ((i-bL)/mL);
	    if ((int) vY[1].x == (int) vY[2].x)
		xL = (int) vY[2].x;
	    xR = (int) ((i-bR)/mR);
	    if ((int) vY[0].x == (int) vY[2].x)
	    	xR = (int) vY[2].x;
	    
	    for (j = xL; j <= xR; j++)
		{
		// calculate the pixel color and draw it.
		  r.setPixel(PixColor(j,i), j, i);
		}
	  }

	}

    public void Tri2(Raster r) // case for no breakpoint, top flat
     {
	double mL;
	double mR;
	double bL;
	double bR;
	int i;
	int j;
	int xR;
	int xL;


	// calculate slopes and intercepts for left and right sides.
	mL = (vY[0].y - vY[2].y)/(vY[0].x - vY[2].x);
	bL =  vY[0].y - mL * vY[0].x;

	mR = (vY[1].y - vY[2].y)/(vY[1].x - vY[2].x);
	bR =  vY[1].y - mR * vY[1].x;

	//for height of triangle....
	for (i = (int) vY[1].y; i < vY[2].y; i++)
	  {
	//calculate left and rightmost points, compensate for infinite slope.
	    xL = (int) ((i-bL)/mL);
	    if ((int) vY[0].x == (int) vY[2].x)
		xL = (int) vY[2].x;
	    xR = (int) ((i-bR)/mR);
	    if ((int) vY[1].x == (int) vY[2].x)
	    	xR = (int) vY[2].x;
	    
	//for width of triangle....
	    for (j = xL; j <= xR; j++)
		{
		//calculate pixel color at point and set it.
		  r.setPixel(PixColor(j,i), j, i);
		}
	  }
	
	}

    public void Tri1(Raster r) // method for no breakpoint, flat bottom.
     {	double mL;
	double mR;
	double bL;
	double bR;
	int i;
	int j;
	int xR;
	int xL;


	//calculate slopes for left and right side.
	mL = (vY[0].y - vY[1].y)/(vY[0].x - vY[1].x);
	bL =  vY[0].y - mL * vY[0].x;

	mR = (vY[0].y - vY[2].y)/(vY[0].x - vY[2].x);
	bR =  vY[0].y - mR * vY[0].x;

	//for height of triangle....
	for (i = (int) vY[0].y; i < vY[1].y; i++)
	  {
	//calculate left and rightmost points; compensate for infinite slopes.
	    xL = (int) ((i-bL)/mL);
	    if ((int) vY[0].x == (int) vY[1].x)
		xL = (int) vY[1].x;
	    xR = (int) ((i-bR)/mR);
	    if ((int) vY[0].x == (int) vY[2].x)
	    	xR = (int) vY[2].x;
	    
	//for width of triangle....
	    for (j = xL; j <= xR; j++)
		{
		//calculate pixel color and set it.
		  r.setPixel(PixColor(j,i), j, i);
		}
	  }

	}

    public int PixColor(int x, int y) //calculate color of pixel at x,y. 
     {
	int pix = 0;
	double pixA = 0;
	double pixR = 0;
	double pixG = 0;
	double pixB = 0;

	//for each color value, compute color value at x,y.
	pixA = ((Aa * x + Ba * y) + Ca);

	pixR = ((Ar * x + Br * y) + Cr);

	pixG = ((Ag * x + Bg * y) + Cg + (1 << 11));

	pixB = ((Ab * x + Bb * y) + Cb + (1 << 11));

	//re-parse color values.
	pixA = (((int) pixA << 24) | 0x00ffffff);

	pixR = (((int) pixR << 16) | 0x00ff0000);

	pixG = (((int) pixG << 8) | 0x0000ff00);

	pixB = ((int) pixB | 0x000000ff);

	//recombine color values and return final value.
	pix = (int)(pixA + pixR + pixG + pixB);
	return pix;
	
     }





}





