import Drawable;
import Vertex3D;

class Triangle implements Drawable {
  // need to store info on relative vertex position
  // type, flat side
  // 0     bottom
  // 1     left
  // 2     top
  // 3     right
  // "top" = upper left vertex
  protected Vertex3D v[];
  private int type, minx, miny, maxx, maxy;
  boolean debug = false;

  public Triangle() {
    v = new Vertex3D[3];
  }

  public Triangle(Vertex3D v0, Vertex3D v1, Vertex3D v2) {
    v = new Vertex3D[3];
    v[0] = v0;
    v[1] = v1;
    v[2] = v2;
  }
    
  public void Draw(Raster r) {
    int i, j, last, top = -1;

    minx = miny = Integer.MAX_VALUE;
    maxx = maxy = Integer.MIN_VALUE;
    last = 0;

    /* if((v[0].x == v[1].x && v[0].y == v[1].y) || 
       (v[1].x == v[2].x && v[1].y == v[2].y) || 
       (v[2].x == v[0].x && v[2].y == v[0].y))
      return;
    */

    for(j=1; j<=5; j++) {
      i = j % 3;
      if(debug) System.out.print("  v#" + i + " x: " + v[i].x + " y: " + v[i].y);
      if(v[i].x <= minx) {	
	minx = (int)(v[i].x);
	//	if(v[i].x == v[last].x)
	  type = 1;
      }
      if(v[i].y <= miny) {
	miny = (int)(v[i].y);
	if(top == -1 || v[i].y < v[top].y || (v[i].y == v[top].y && v[i].x > v[top].x))
	  top = i;
	if(v[i].y == v[last].y)
	  type = 2;
      }
      if(v[i].x >= maxx) {
	maxx = (int)(v[i].x);
	//	if(v[i].x == v[last].x)
	  type = 3;
      }
      if(v[i].y >= maxy) {
	maxy = (int)(v[i].y);
	if(v[i].y == v[last].y)
	  type = 0;
      }
      last = i;
    }
    //top = (top + 2) % 3; // may need case for flat tops
    if(debug) System.out.println("\ntop: " + top + " type: " + type + " minx: " + minx + 
				 " miny: " + miny + " maxx: " + maxx + " maxy: " + maxy);

    int vv1l, vv2l;
    double ml, xl, dxl, dyl; // ok, not really m anymore
    int vv1r, vv2r;
    double mr, xr, dxr, dyr; // ok, not really m anymore
    InterColor cl, cr;
    InterZ zl, zr;

    // setup left, right edges
    if(type == 2) {
      vv1r = top;
      vv1l = (vv1r + 2) % 3;
    } else {
      vv1r = vv1l = top;
    }
    vv2r = (vv1r + 1) % 3;
    vv2l = (vv1l + 2) % 3;

    dxl = v[vv1l].x - v[vv2l].x;
    dyl = v[vv1l].y - v[vv2l].y;
    ml = dxl/dyl;
    xl = v[vv1l].x;
    cl = new InterColor(v[vv1l].argb, v[vv2l].argb, -dyl);
    zl = new InterZ(v[vv1l].z, v[vv2l].z, -dyl);
    
    dxr = v[vv1r].x - v[vv2r].x;
    dyr = v[vv1r].y - v[vv2r].y;
    mr = dxr/dyr;
    xr = v[vv1r].x;
    cr = new InterColor(v[vv1r].argb, v[vv2r].argb, -dyr);
    zr = new InterZ(v[vv1r].z, v[vv2r].z, -dyr);

    if(debug) System.out.println("xl: " + xl + " ml: " + ml + " xr: " + xr + " mr: " + mr);
    
    // step down, finding edges
    for(i=miny; i<=maxy; i++) {
      // watch for breakpoints
      if(type == 1 || type == 3) {
	if(i>=v[vv2l].y) {
	  vv1l = vv2l;
	  vv2l = (vv2l + 2) % 3;
	  dxl = v[vv1l].x - v[vv2l].x;
	  dyl = v[vv1l].y - v[vv2l].y;
	  ml = dxl/dyl;
	  xl = v[vv1l].x;
	  cl = new InterColor(v[vv1l].argb, v[vv2l].argb, -dyl);
	  zl = new InterZ(v[vv1l].z, v[vv2l].z, -dyl);
	}
	if(i>=v[vv2r].y) {
	  vv1r = vv2r;
	  vv2r = (vv2r + 1) % 3;
	  dxr = v[vv1r].x - v[vv2r].x;
	  dyr = v[vv1r].y - v[vv2r].y;
	  mr = dxr/dyr;
	  xr = v[vv1r].x;
	  cr = new InterColor(v[vv1r].argb, v[vv2r].argb, -dyr);
	  zr = new InterZ(v[vv1r].z, v[vv2r].z, -dyr);
	}
      }
      // make, draw scan lines
      InterColor c = new InterColor(cl.getVal(i-(int)v[vv1l].y), cr.getVal(i-(int)v[vv1r].y), xr - xl);
      InterZ z = new InterZ(zl.getVal(i-(int)v[vv1l].y), zr.getVal(i-(int)v[vv1r].y), xr - xl);

      for(j=(int)Math.ceil(xl); j<=xr; j++)
	((ZRaster)r).setPixel(c.getVal(j - (int)Math.ceil(xl)), j, i, z.getVal(j - (int)Math.ceil(xl)));

      xl += ml;
      xr += mr;
      if(ml < 0 && xl < v[vv2l].x) xl = v[vv2l].x;
      if(ml > 0 && xl > v[vv2l].x) xl = v[vv2l].x;
      if(mr < 0 && xr < v[vv2r].x) xr = v[vv2r].x;
      if(mr > 0 && xr > v[vv2r].x) xr = v[vv2r].x;
    }
  }

  private int findtop() {
    if(v[0].y <= v[1].y && v[0].y <= v[2].y)
      return 0;
    if(v[1].y <= v[0].y && v[1].y <= v[2].y)
      return 1;
    if(v[2].y <= v[0].y && v[2].y <= v[1].y)
      return 2;
    return -1;
  }
}

class InterColor {
  private int a1, r1, g1, b1;
  private int a2, r2, g2, b2;
  private double da, dr, dg, db;

  public InterColor(int c1, int c2, double dist) {
    a1 = (c1 >>> 24) & 255;
    r1 = (c1 >>> 16) & 255;
    g1 = (c1 >>> 8) & 255;
    b1 = c1 & 255;
    a2 = (c2 >>> 24) & 255;
    r2 = (c2 >>> 16) & 255;
    g2 = (c2 >>> 8) & 255;
    b2 = c2 & 255;

    da = ((double)(a2 - a1)) / dist;
    dr = ((double)(r2 - r1)) / dist;
    dg = ((double)(g2 - g1)) / dist;
    db = ((double)(b2 - b1)) / dist;
  }

  public int getVal(int dist) {
    int ra = (int)(a1 + da * ((double)dist));
    int rr = (int)(r1 + dr * ((double)dist));
    int rg = (int)(g1 + dg * ((double)dist));
    int rb = (int)(b1 + db * ((double)dist));

    /*    if((da > 0) && (ra > a2)) ra = (int)a2;
    if((dr > 0) && (rr > r2)) rr = (int)r2;
    if((dg > 0) && (rg > g2)) rg = (int)g2;
    if((db > 0) && (rb > b2)) rb = (int)b2;
    if((da < 0) && (ra < a1)) ra = (int)a1;
    if((dr < 0) && (rr < r1)) rr = (int)r1;
    if((dg < 0) && (rg < g1)) rg = (int)g1;
    if((db < 0) && (rb < b1)) rb = (int)b1;
    */

    if(ra > 255) ra = 255;
    if(rr > 255) rr = 255;
    if(rg > 255) rg = 255;
    if(rb > 255) rb = 255;

    return (((ra & 255) << 24) +
	    ((rr & 255) << 16) +
	    ((rg & 255) << 8) +
	    (rb & 255));
  } 
}

class InterZ {
  private float z1, z2;
  private double dz;

  public InterZ(float iz1, float iz2, double dist) {
    z1 = iz1;
    z2 = iz2;

    dz = ((double)(z2 - z1)) / dist;
  }

  public int getVal(int dist) {
    return (int)(z1 + dz * ((double)dist));
  } 
}
