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


class Triangle implements Drawable {
  protected Vertex2D v[];
  protected float dya, dyr, dyg, dyb, dxa, dxr, dxg, dxb;
  protected int ava, avr, avg, avb;
  protected Vertex2D ysort[], xsort[];
  protected boolean slopeMemo = false;
  public Triangle() {
  }
  
  public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) {
    v = new Vertex2D[3];
    v[0] = v0;
    v[1] = v1;
    v[2] = v2;
  }
  
  public float SubDraw(Raster r, Vertex2D v1a, Vertex2D v1b,
		    Vertex2D v2a, Vertex2D v2b, float yprime){
    // this will return the ycoord of the breakpoint so we know where
    // to continue from
    Vertex2D top1, bot1, top2, bot2;
    float slope1, slope2, x1, x2;
    float tmpslp1, tmpslp2, tmpx1, tmpx2;
    int x, y, pik;
    
    y = (int)yprime+1;//not actually a rounding error
    tmpslp1 = (v1b.x-v1a.x)/(v1b.y-v1a.y);
    tmpslp2 = (v2b.x-v2a.x)/(v2b.y-v2a.y);
    tmpx1 = v1a.x + (((float)y)-v1a.y)*tmpslp1; // and we set x's position here
    tmpx2 = v2a.x + (((float)y)-v2a.y)*tmpslp2;
    
    // 1 is left, 2 is right
    if ((tmpx1 > tmpx2) || (v1b.x > v2b.x)) {
      // switch!
      top1 = v2a;
      bot1 = v2b;
      top2 = v1a;
      bot2 = v1b;
      slope1 = tmpslp2;
      slope2 = tmpslp1;
      x1 = tmpx2;
      x2 = tmpx1;
    } else {
      top1 = v1a;
      bot1 = v1b;
      top2 = v2a;
      bot2 = v2b;
      slope1 = tmpslp1;
      slope2 = tmpslp2;
      x1 = tmpx1;
      x2 = tmpx2;
    }

    while ((y <=bot1.y) && (y <= bot2.y)) {
      for (x = (int)x1+1; x <= (int)x2; x++){
	// always check from the center for pixel colors
	pik = getColor((float)x+(float)0.5, (float)y+(float)0.5);
	r.setPixel(pik,x,y);
      }
      // special case for ends of line
      if (x2 >= (float)(Math.floor((double)x2)+0.5)){
	pik = getColor(((float)(Math.floor((double)x2)+0.5)), (float)y+(float)0.5);
	r.setPixel(pik, (int)Math.floor((double)x2), y);
      }
      if (x1 <= (float)(Math.floor((double)x1)+0.5)){
	pik = getColor(((float)(Math.floor((double)x1)+0.5)), (float)y+(float)0.5);
	r.setPixel(pik, (int)Math.floor((double)x1), y);
      }
      // now we need to advance to the next line
      y++;
      x1 += slope1;
      x2 += slope2;
    }
    
    // remember, send back the breakpoint
    if (bot1.y <= bot2.y){
      return(bot1.y);
    } else {
      return(bot2.y);
    }   
  }
  
  public int getColor(float x, float y) {
    if (!slopeMemo){
      int da, dr, dg, db;
      float distance;
      ysort = new Vertex2D[3];
      if (v[0].y < v[1].y) {
	if (v[0].y < v[2].y) {
	  ysort[0] = v[0];
	  if (v[1].y < v[2].y) {
	    ysort[1] = v[1];
	    ysort[2] = v[2];
	  } else {
	    ysort[1] = v[2];
	    ysort[2] = v[1];
	  }
	} else {
	  ysort[0] = v[2];
	  ysort[1] = v[0];
	  ysort[2] = v[1];
	}
      } else {
	// v[0].y >= v[1].y
	if (v[1].y < v[2].y) {
	  ysort[0] = v[1];
	  if (v[2].y < v[0].y) {
	    ysort[1] = v[2];
	    ysort[2] = v[0];
	  } else {
	    ysort[1] = v[0];
	    ysort[2] = v[2];
	  }
	} else {
	  ysort[0] = v[2];
	  ysort[1] = v[1];
	  ysort[2] = v[0];
	}
      }
      // now ysort[] is sorted in y. lets define the
      // edge from ysort[0] to ysort[2] as dealing with dy, and 
      // xsort[0] to xsort[1]
      // as dealing with dx. If the triangle is degenerate, it's going
      // to suck anyway.
      float dist = 
	(ysort[2].y-ysort[0].y);
      da = ((ysort[2].argb >>> 24) & 0xFF) - 
	((ysort[0].argb >>> 24) & 0xFF);
      dr = ((ysort[2].argb >>> 16) & 0xFF) - 
	((ysort[0].argb >>> 16) & 0xFF);
      dg = ((ysort[2].argb >>> 8) & 0xFF) - 
	((ysort[0].argb >>> 8) & 0xFF);
      db = (ysort[2].argb & 0xFF) - 
	(ysort[0].argb & 0xFF);
      
      dya = (float)da/dist;
      dyr = (float)dr/dist;
      dyg = (float)dg/dist;
      dyb = (float)db/dist;

      xsort = new Vertex2D[3];
      if (v[0].x < v[1].x) {
	if (v[0].x < v[2].x) {
	  xsort[0] = v[0];
	  if (v[1].x < v[2].x) {
	    xsort[1] = v[1];
	    xsort[2] = v[2];
	  } else {
	    xsort[1] = v[2];
	    xsort[2] = v[1];
	  }
	} else {
	  xsort[0] = v[2];
	  xsort[1] = v[0];
	  xsort[2] = v[1];
	}
      } else {
	// v[0].x >= v[1].x
	if (v[1].x < v[2].x) {
	  xsort[0] = v[1];
	  if (v[2].x < v[0].x) {
	    xsort[1] = v[2];
	    xsort[2] = v[0];
	  } else {
	    xsort[1] = v[0];
	    xsort[2] = v[2];
	  }
	} else {
	  xsort[0] = v[2];
	  xsort[1] = v[1];
	  xsort[2] = v[0];
	}
      }
      // now xsort[] is sorted in x. lets define the
      // edge from ysort[0] to ysort[2] as dealing with dy, and 
      // xsort[0] to xsort[1]
      // as dealing with dx. If the triangle is degenerate, it's going
      // to suck anyway.
      dist = 
	//	(ysort[2].y-ysort[0].y);
	(xsort[2].x-xsort[0].x);
      da = ((xsort[2].argb >>> 24) & 0xFF) - 
	((xsort[0].argb >>> 24) & 0xFF);
      dr = ((xsort[2].argb >>> 16) & 0xFF) - 
	((xsort[0].argb >>> 16) & 0xFF);
      dg = ((xsort[2].argb >>> 8) & 0xFF) - 
	((xsort[0].argb >>> 8) & 0xFF);
      db = (xsort[2].argb & 0xFF) - 
	(xsort[0].argb & 0xFF);
      
      dxa = (float)da/dist;
      dxr = (float)dr/dist;
      dxg = (float)dg/dist;
      dxb = (float)db/dist;
      slopeMemo = true;
    }
    // dx is measured from xsort[0]. dy is measured from ysort[0].
    float dx, dy;
    int a, r, g, b, pik;
    dx = x - xsort[0].x;
    dy = y - ysort[0].y;
    
    if (ysort[0].distance(x,y) <= xsort[0].distance(x,y)){
    // scan down dy from ysort[0] then across dx to (x,y)
    // (it sure would be nice if this worked)
    //a = (int)(/*((ysort[0].argb >>> 24) & 0xFF) +*/ dy*dya + dx*dxa) & 0xFF;
    a = 0xFF;
    r = (int)(((ysort[0].argb >>> 16) & 0xFF) + dy*dyr + dx*dxr) & 0xFF;
    g = (int)(((ysort[0].argb >>>  8) & 0xFF) + dy*dyg + dx*dxg) & 0xFF;
    b = (int)((ysort[0].argb & 0xFF) + dy*dyb + dx*dxb) & 0xFF;
    // and put them in pik
    pik = (a << 24) + (r << 16) + (g << 8) + b;
    return(pik);
    } else {
    //a = (int)(((xsort[0].argb >>> 24) & 0xFF) + dy*dya + dx*dxa) & 0xFF;
    a = 0xFF;
    r = (int)(((xsort[0].argb >>> 16) & 0xFF) + dy*dyr + dx*dxr) & 0xFF;
    g = (int)(((xsort[0].argb >>>  8) & 0xFF) + dy*dyg + dx*dxg) & 0xFF;
    b = (int)((xsort[0].argb & 0xFF) + dy*dyb + dx*dxb) & 0xFF;
    // and put them in pik
    pik = (a << 24) + (r << 16) + (g << 8) + b;
    return(pik);
    }
  }


  public int getColorBadly(float x, float y) {
    // return v[0].argb;
    int a1, a2, a0, r1, r2, r0, g1, g2, g0, b1, b2, b0;//source
    int a, r, g, b; //target
    int pik; //return value
    float d1, d2, d0; //distances
    float dsum; //sum of distances for calculation purposes
    
    // Snag them distances
    d0 = v[0].distance(x,y);
    d1 = v[1].distance(x,y);
    d2 = v[2].distance(x,y);
    
    dsum = d0+d1+d2;
    
    // OK. now let's get the initial values somewhere we can play with them.
    a0 = (v[0].argb >>> 24) & 0xFF; a1 = (v[1].argb >>> 24) & 0xFF; a2 = (v[2].argb >>> 24) & 0xFF;
    r0 = (v[0].argb >>> 16) & 0xFF; r1 = (v[1].argb >>> 16) & 0xFF; r2 = (v[2].argb >>> 16) & 0xFF;
    g0 = (v[0].argb >>> 8) & 0xFF; g1 = (v[1].argb >>> 8) & 0xFF; g2 = (v[2].argb >>> 8) & 0xFF;
    b0 = (v[0].argb) & 0xFF; b1 = (v[1].argb) & 0xFF; b2 = (v[2].argb) & 0xFF;
    
    // Let us simply weight those color values. First, we'll calculate
    // the overall weights and throw them in the d? variables.

    d0 /= dsum; d1 /= dsum; d2 /= dsum;

    // Now we multiply the colorlet values by the weight

    a0 = (int)(a0*d0); r0 = (int)(r0*d0); g0 = (int)(g0*d0); b0 = (int)(b0*d0);
    a1 = (int)(a1*d1); r1 = (int)(r1*d1); g1 = (int)(g1*d1); b1 = (int)(b1*d1);
    a2 = (int)(a2*d2); r2 = (int)(r2*d2); g2 = (int)(g2*d2); b2 = (int)(b2*d2);

    // now we add up the values
    a = a0+a1+a2; r=r0+r1+r2; g=g0+g1+g2; b=b0+b1+b2;
    
    // and put them in pik
    pik = (a << 24) + (r << 16) + (g << 8) + b;
    return(pik);

  }

    
  public void Draw(Raster r) {
    Vertex2D sort[];
    float breaky;

    // Step 1: sort vertices. We only actually need to sort them in y;
    // we'll divide the triangle into one or two 'special case' SubDraw
    // calls. The number of SubDraw calls depends on the existance of
    // a breakpoint.
    sort = new Vertex2D[3];
    if (v[0].y < v[1].y) {
      if (v[0].y < v[2].y) {
	sort[0] = v[0];
	if (v[1].y < v[2].y) {
	 sort[1] = v[1];
	 sort[2] = v[2];
	} else {
	  sort[1] = v[2];
	  sort[2] = v[1];
	}
      } else {
	sort[0] = v[2];
        sort[1] = v[0];
	sort[2] = v[1];
      }
    } else {
      // v[0].y >= v[1].y
      if (v[1].y < v[2].y) {
	sort[0] = v[1];
	if (v[2].y < v[0].y) {
	  sort[1] = v[2];
	  sort[2] = v[0];
	} else {
	  sort[1] = v[0];
	  sort[2] = v[2];
	}
      } else {
	sort[0] = v[2];
	sort[1] = v[1];
	sort[2] = v[0];
      }
    }
    // now sort[] contains pointers to the vertices in sorted order.
    // so, if sort[0].y == sort[1].y OR sort[1].y == sort[2].y, we've got
    // a horizontal boundary and thus no breakpoint.
    // test for weirdness
    if (sort[0].y == sort[1].y){
      SubDraw(r, sort[0], sort[2], sort[1], sort[2], sort[0].y);
      System.out.println("Case 1");
      //sort[2].y is the largest y coordinate, by definition
    } else {
      if (sort[1].y == sort[2].y) {
	SubDraw(r, sort[0], sort[1], sort[0], sort[2], sort[0].y);
	System.out.println("Case 2");
      } else {
	// no special case, oh well, I guess I'm sad 
	breaky = SubDraw(r, sort[0], sort[1], sort[0], sort[2], sort[0].y);
	//System.out.println("Case 3a");
	SubDraw(r, sort[0], sort[2], sort[1], sort[2], breaky);
	//System.out.println("Case 3b");
      }
    }
    // and that's about it.
 }


  //    public void Draw(Raster r) {
  //      r.setPixel(v[0].argb, (int) v[0].x, (int) v[0].y);
  //      r.setPixel(v[1].argb, (int) v[1].x, (int) v[1].y);
  //      r.setPixel(v[2].argb, (int) v[2].x, (int) v[2].y);
  //}
}
