import Drawable;
import Vertex2D;
import Raster;

class Triangle implements Drawable {
  protected static final int ASHIFT = 24;
  protected static final int RSHIFT = 16;
  protected static final int GSHIFT = 8;
    
  protected Vertex2D v[];

  private boolean bLeft, bColor;
  private float Slope10, Slope20, Slope21;
  private int iColor;

  private float r[], g[], a[], b[];
  
  public Triangle() {
  }

  public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) 
  {
    // Declarations
    float calc[][];
    int c0, c1, c2;

    // Initializations
    // Deal with the color
    if(v0.argb == v1.argb && v1.argb == v2.argb)
      {
	bColor = true;
	iColor = v1.argb;
      }
    else
      {
	bColor = false;
	
	// Need to do the color computations
	// Figure out the matrix
	calc = new float[2][3];
	r = new float[4];
	a = new float[4];
	g = new float[4];
	b = new float[4];

	calc[0][0] = v1.x - v0.x;
	calc[0][1] = v1.y - v0.y;
	calc[1][0] = v2.x - v0.x;
	calc[1][1] = v2.y - v0.y;
	
	// Do the red
	c0 = (v0.argb >> RSHIFT) & 0xff;
	c1 = (v1.argb >> RSHIFT) & 0xff;
	c2 = (v2.argb >> RSHIFT) & 0xff;
	calc[0][2] = c1 - c0;
	calc[1][2] = c2 - c0;
	r[0] = (calc[0][1] * calc[1][2]) - (calc[0][2] * calc[1][1]);
	r[1] = (calc[0][2] * calc[1][0]) - (calc[0][0] * calc[1][2]);
	r[2] = (calc[0][0] * calc[1][1]) - (calc[0][1] * calc[1][0]);
	r[3] = (r[0] * v0.x) + (r[1] * v0.y) + (r[2] * c0);

	// Do the alpha
	c0 = v0.argb >>> ASHIFT;
	c1 = v1.argb >>> ASHIFT;
	c2 = v2.argb >>> ASHIFT;
	calc[0][2] = c1 - c0;
	calc[1][2] = c2 - c0;
	a[0] = (calc[0][1] * calc[1][2]) - (calc[0][2] * calc[1][1]);
	a[1] = (calc[0][2] * calc[1][0]) - (calc[0][0] * calc[1][2]);
	a[2] = (calc[0][0] * calc[1][1]) - (calc[0][1] * calc[1][0]);
	a[3] = (a[0] * v0.x) + (a[1] * v0.y) + (a[2] * c0);

	// Do the green
	c0 = (v0.argb >> GSHIFT) & 0xff;
	c1 = (v1.argb >> GSHIFT) & 0xff;
	c2 = (v2.argb >> GSHIFT) & 0xff;
	calc[0][2] = c1 - c0;
	calc[1][2] = c2 - c0;
	g[0] = (calc[0][1] * calc[1][2]) - (calc[0][2] * calc[1][1]);
	g[1] = (calc[0][2] * calc[1][0]) - (calc[0][0] * calc[1][2]);
	g[2] = (calc[0][0] * calc[1][1]) - (calc[0][1] * calc[1][0]);
	g[3] = (g[0] * v0.x) + (g[1] * v0.y) + (g[2] * c0);

	// Do the blue
	c0 = v0.argb & 0xff;
	c1 = v1.argb & 0xff;
	c2 = v2.argb & 0xff;
	calc[0][2] = c1 - c0;
	calc[1][2] = c2 - c0;
	b[0] = (calc[0][1] * calc[1][2]) - (calc[0][2] * calc[1][1]);
	b[1] = (calc[0][2] * calc[1][0]) - (calc[0][0] * calc[1][2]);
	b[2] = (calc[0][0] * calc[1][1]) - (calc[0][1] * calc[1][0]);
	b[3] = (b[0] * v0.x) + (b[1] * v0.y) + (b[2] * c0);	
      }
    
    v = new Vertex2D[3];
    
    // Sort the vertices in y
    if(v0.y <= v1.y && v0.y <= v2.y)
      {
	v[0] = v0;
	if(v1.y <= v2.y)
	  {
	    v[1] = v1;
	    v[2] = v2;
	  }
	else
	  { 
	    v[1] = v2;
	    v[2] = v1;
	  }
      }
    else if(v1.y <= v0.y && v1.y <= v2.y)
      {
	v[0] = v1;	
	if(v0.y <= v2.y)
	  {
	    v[1] = v0;
	    v[2] = v2;
	  }
	else
	  { 
	    v[1] = v2;
	    v[2] = v0;
	  }
      }
    else
      {
	v[0] = v2;
	if(v0.y <= v1.y)
	  {
	    v[1] = v0;
	    v[2] = v1;
	  }
	else
	  { 
	    v[1] = v1;
	    v[2] = v0;
	  }
      }

    // Determine the line slopes
    Slope10 = (v[1].x - v[0].x) / (v[1].y - v[0].y); 
    Slope20 = (v[2].x - v[0].x) / (v[2].y - v[0].y); 
    Slope21 = (v[2].x - v[1].x) / (v[2].y - v[1].y); 

    // Determine whether we're playing with a left-sided or a right-sided
    // triangle: this will be used to determine which edge equation is used
    // for the scan start and finish.
    
    if(v[1].x <= v[0].x && v[1].x <= v[2].x)
      bLeft = true;
    else if(v[1].x <= v[0].x && v[1].x >= v[2].x)
      if(Slope10 < Slope20)
	bLeft = true;
      else bLeft = false;
    else if(v[1].x >= v[0].x && v[1].x <= v[2].x)
      if(Slope10 > Slope20)
	bLeft = false;
      else bLeft = true;
    else 
      bLeft = false;
  }
  
  public void Draw(Raster ras)
  {
    // Declarations
    int yVal, xVal;
    int xR;
    float lSlope, rSlope;
    double xLeft, xRight;
    int iScolor;
    float Blue, Green, Red, Alpha;
    float iBlue, iRed, iGreen, iAlpha;
    
    // Set the vertex pixels
    ras.setPixel(v[0].argb, (int) v[0].x, (int) v[0].y);
    ras.setPixel(v[1].argb, (int) v[1].x, (int) v[1].y);
    ras.setPixel(v[2].argb, (int) v[2].x, (int) v[2].y);
    
    if(bColor)
      {
	if(bLeft)
	  {
	    lSlope = Slope10; rSlope = Slope20;
	  }
	else
	  {
	    lSlope = Slope20; rSlope = Slope10;
	  }
	
	for(yVal=(int)Math.ceil(v[0].y);
	    yVal<=(int)v[1].y;yVal++)
	  {
	    xLeft = (yVal - v[0].y)*lSlope + v[0].x;
	    //xRight = (yVal - v[0].y)*rSlope + v[0].x;	
	    xR = (int)((yVal - v[0].y)*rSlope + v[0].x);	
	    
	    for(xVal=(int)Math.ceil(xLeft);xVal<=xR;xVal++)
	      ras.setPixel(iColor, xVal, yVal);
	  }
	
	if(bLeft)
	  lSlope = Slope21;
	else
	  rSlope = Slope21;
	
	for(yVal=(int)Math.ceil(v[1].y);yVal<=(int)v[2].y;yVal++)
	  {
	    if(bLeft)
	      {
		xLeft = (yVal - v[1].y)*lSlope + v[1].x;
		//xRight = (yVal - v[0].y)*rSlope + v[0].x;
		xR = (int)((yVal - v[0].y)*rSlope + v[0].x);	
	      }
	    else
	      {
		xLeft = (yVal - v[0].y)*lSlope + v[0].x;
		//xRight = (yVal - v[1].y)*rSlope + v[1].x;
		xR = (int)((yVal - v[1].y)*rSlope + v[1].x);	
	      }
	    
	    for(xVal=(int)Math.ceil(xLeft);xVal<=xR;xVal++)
	      ras.setPixel(iColor, xVal, yVal);
	  }
      }
    else
      {
	if(bLeft)
	  {
	    lSlope = Slope10; rSlope = Slope20;
	  }
	else
	  {
	    lSlope = Slope20; rSlope = Slope10;
	  }
	
	for(yVal=(int)Math.ceil(v[0].y);yVal<=(int)v[1].y;yVal++)
	  {
	    xLeft = (yVal - v[0].y)*lSlope + v[0].x;
	    xRight = (yVal - v[0].y)*rSlope + v[0].x;	

	    xVal=(int)Math.ceil(xLeft);

	    Blue = (b[3] - (b[0] * xVal) - (b[1] * yVal)) / b[2];
	    Red = (r[3] - (r[0] * xVal) - (r[1] * yVal)) / r[2];
	    Green = (g[3] - (g[0] * xVal) - (g[1] * yVal)) / g[2];
	    // HACK THE ALPHA CHANNEL: IT'S ALWAYS 255 anyway
	    //Alpha = (a[3] - (a[0] * xLeft) - (a[1] * yVal)) / a[2];

	    iBlue = b[0] / b[2];
	    iRed = r[0] / r[2];
	    iGreen = g[0] / g[2];
	    //iAlpha = a[0] / a[2];
	    
	    for(;xVal<=(int)xRight;xVal++)
	      {
		// Get the color
		iScolor = (0xff000000 | (int)(0.5 + Red) << RSHIFT | 
			   (int)(0.5 + Green) << GSHIFT | (int)(0.5 + Blue));
		ras.setPixel(iScolor, xVal, yVal);
		Blue -= iBlue;
		Green -= iGreen;
		Red -= iRed;
		//Alpha -= iAlpha;
	      }
	  }
	
	if(bLeft)
	  lSlope = Slope21;
	else
	  rSlope = Slope21;
	
	for(yVal=(int)Math.ceil(v[1].y);yVal<=(int)v[2].y;yVal++)
	  {
	    if(bLeft)
	      {
		xLeft = (yVal - v[1].y)*lSlope + v[1].x;
		xRight = (yVal - v[0].y)*rSlope + v[0].x;
	      }
	    else
	      {
		xLeft = (yVal - v[0].y)*lSlope + v[0].x;
		xRight = (yVal - v[1].y)*rSlope + v[1].x;
	      }
	    
	    xVal=(int)Math.ceil(xLeft);
	      
	    Blue = (b[3] - (b[0] * xVal) - (b[1] * yVal)) / b[2];
	    Red = (r[3] - (r[0] * xVal) - (r[1] * yVal)) / r[2];
	    Green = (g[3] - (g[0] * xVal) - (g[1] * yVal)) / g[2];
	    //Alpha = a[3]/a[2] - (a[0] * xVal)/a[2] - (a[1] * yVal)/a[2];
	    
	    iBlue = b[0] / b[2];
	    iRed = r[0] / r[2];
	    iGreen = g[0] / g[2];
	    //iAlpha = a[0] / a[2];

	    for(;xVal<=(int)xRight;xVal++)
	      {
		// Get the color
		iScolor = (0xff000000 | (int)(Red + 0.5) << RSHIFT | 
			   (int)(Green + 0.5) << GSHIFT | (int)(0.5 + Blue));
		ras.setPixel(iScolor, xVal, yVal);
		Blue -= iBlue;
		Green -= iGreen;
		Red -= iRed;
		//Alpha -= iAlpha;
	      }
	  }
      }
  }
}




