import java.awt.*;
import java.lang.*;

public class Triangle2D implements Drawable {
  
  protected Vertex2D v[];
  protected EdgeEqn edge[];
  protected float twiceArea;
  protected static byte sort[][]= {{0, 1},{1, 2},{0, 2},{2, 0},{2, 1},{1, 0}};
  protected int vtxSortX[];
  protected int vtxSortY[];
  protected int noseNegY, nosePosY, ltBreak, rtBreak;
  protected int activeEdge[];
  
  ///////  CONSTRUCTORS /////////////////////////////  
  
  // zero argument constructor for future expansion 
  public Triangle2D() {;}

  
  // constructor using three Vertex2D 
  public Triangle2D( Vertex2D v0,  Vertex2D v1,  Vertex2D v2 ) {
    v = new Vertex2D[3];
    v[0]=v0;    v[1]=v1;  v[2]=v2;
    v[0].setVertexNo(0);
    v[1].setVertexNo(1); 
    v[2].setVertexNo(2);
    vtxSortX = new int[3]; vtxSortY = new int[3];
    noseNegY= -1;    nosePosY= -1;
    ltBreak = -1;    rtBreak = -1;
    activeEdge = new int[2];
  }	
  
  /////////  METHODS  ////////////////////////////
 
  protected boolean triangleSetup(Raster r) {
      //System.out.println("IN TRIANGLESETP(RASTER)" );
    if (edge == null) edge = new EdgeEqn[3];
    
    //Compute the three edge equations  
    edge[2] = new EdgeEqn(2, v[0], v[1]);
    edge[0] = new EdgeEqn(0, v[1], v[2]);
    edge[1] = new EdgeEqn(1, v[2], v[0]);
    
    //Trick #1: Orient edges so that the triangle's interior lies within all of
    //their positive half-spaces. Assuring that the area is positive accomplishes this
    twiceArea = edge[0].getC() + edge[1].getC() + edge[2].getC();
    if (twiceArea  < 0) {
      edge[0].flip();    edge[1].flip();    edge[2].flip();
      twiceArea = -twiceArea ;
    }
   //       if ( twiceArea < 1.0f){
// 	     // System.out.println("HAS TO RETURN FALSE");
//  	return false;      //>0 is implicit. 
//      }

	 //System.out.println("IF TWICEAREA < 1, SHOULDN'T RUN THIS");
    //area of smallest triangle = 0.5*1*1

    //for(int j=0; j<3; j++ ) System.out.println("edge["+j+"].A=" +  edge[j].getA() + "B=" +  edge[j].getB()+ "C=" +  edge[j].getC()   );
    
    //Trick #2: compute bounding box 
    int xflag = edge[2].flag + 2*edge[0].flag + 4*edge[1].flag;
    int yflag = (xflag >> 3) - 1;
    xflag = (xflag & 7) - 1;
    vtxSortX[0] = sort[xflag][0];
    vtxSortX[2] = sort[xflag][1];
    vtxSortX[1] = 3 - ( vtxSortX[0] + vtxSortX[2] );
    vtxSortY[0] = sort[yflag][1];
    vtxSortY[2] = sort[yflag][0];
   vtxSortY[1] = 3 - ( vtxSortY[0] + vtxSortY[2] );

//     for( int kk=0; kk<3; kk++){
// 	System.out.println("vtxSortX["+kk+"]= "  +    vtxSortX[kk]  );
// 	System.out.println("vtxSortY["+kk+"]= "  +    vtxSortY[kk]  );
//     }

     isNoBreakPoint();

    /*   
    //clip triangle's bounding box to raster
    xMin = (xMin < 0) ? 0 : xMin;
    xMax = (xMax >= r.width) ? r.width - 1 : xMax;
    yMin = (yMin < 0) ? 0 : yMin;
    yMax = (yMax >= r.height) ? r.height - 1 : yMax;
    */
       // System.out.println("noseNegY="+ noseNegY + "nosePosY="+ nosePosY + "ltBreak=" +ltBreak + "rtBreak=" + rtBreak);
   
     return true;
  }
  


  protected void isNoBreakPoint() {
      //System.out.println("IN ISNOBREAKPOINT" );
    int i=0;
    int flag = 0;
    do {
      if( edge[i].horiz ) {
	flag = 1;
	noseNegY = i;
      }
      i++;
    } while( (flag==0) && (i<3) );
    if( flag==1 ){
      if( noseNegY==vtxSortY[0] ){
	 return; 
      }
      else {
	nosePosY=noseNegY; noseNegY= -1;
	return; 
      }
    }
    float x = v[vtxSortY[1]].getx(); 
    float y = v[vtxSortY[1]].gety(); 
    if( (vtxSortY[1] !=vtxSortX[2])&&(edge[vtxSortY[1]].findXAtGivenY(y) > x) ) {
      ltBreak = vtxSortY[1];
      return;
    }
    if( (vtxSortY[1] !=vtxSortX[0])&&(edge[vtxSortY[1]].findXAtGivenY(y) < x  )  ) {
      rtBreak = vtxSortY[1];
      return;
    }
    return;
  } //ISNOBREAKPOINT


 //this will always return first the edge closer to x=0 and then the one further away
  public void getActiveEdges( ) {
      //System.out.println("IN GETACTIVEEDGES(void)" );
    if( noseNegY >= 0 ) {
      activeEdge[0] = getEdgeNo( noseNegY+1 );
      activeEdge[1] = getEdgeNo( noseNegY-1 ); 
      return;
    }
    if( nosePosY >= 0 ) {
      activeEdge[0] = getEdgeNo( nosePosY-1 );
      activeEdge[1] = getEdgeNo( nosePosY+1 ); 
      return;
    }
    return;
  }//GETACTIVEEDGES



  //this will always return first the edge closer to x=0 and then the one further away
  public void getActiveEdges( boolean isBeforeBreak) {
      //System.out.println("IN GETACTIVEEDGES(isBeforeBreak)" );
    if( ltBreak >= 0) {
      if( isBeforeBreak ) {
	activeEdge[0] = getEdgeNo( ltBreak-1 );
	activeEdge[1] = getEdgeNo( ltBreak );
      }
      else {
	activeEdge[0] = getEdgeNo( ltBreak+1 );
	activeEdge[1] = getEdgeNo( ltBreak );
      }
      return;
    }
    if( rtBreak >= 0) {
      if( isBeforeBreak ) {
	activeEdge[0] = getEdgeNo( rtBreak );
	activeEdge[1] = getEdgeNo( rtBreak+1 );
      }
      else {
	activeEdge[0] = getEdgeNo( rtBreak );
	activeEdge[1] = getEdgeNo( rtBreak-1 );
      }
      return;
    }
    return;
  }//GETACTIVEEDGES



  //returns edge number between 0 and 2, irrespective of "int someNo"
  public int getEdgeNo( int someNo ){
    if( someNo >= 3 ) return( getEdgeNo(someNo-3) );
    if( someNo < 0 ) return( getEdgeNo(someNo+3) );
    return someNo;
  }


   
  public int getVertexNo(int someNo ){
    if( someNo >= 3 ) return( getVertexNo(someNo-3) );
    if( someNo < 0 ) return( getVertexNo(someNo+3) );
    return someNo;
  }


  

  public void draw( Raster r) {
      
      //System.out.println("IN TRIANGLE2D.DRAW" );
    if( ! this.triangleSetup(r)){
	//System.out.println("TWICE AREA <1 => DRAW EDGE/VERTEX" );
	for(int i=0; i<3; i++ ) edge[i].drawLine(r);
	return;
    }


    //System.out.println("WILL DRAW SIMPLE TRIANGLE" );
    if( noseNegY >= 0){
      getActiveEdges();
      drawSpan( activeEdge, v[ getVertexNo(noseNegY) ], v[ getVertexNo(noseNegY-1) ], r );
    }
    if( nosePosY >= 0){
      getActiveEdges(); 
      drawSpan( activeEdge, v[ getVertexNo(nosePosY+1) ], v[ getVertexNo(nosePosY) ], r ); 
    }
    if( ltBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak+1) ], v[ getVertexNo(ltBreak) ], r );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak) ], v[ getVertexNo(ltBreak-1) ], r );
    }
    if( rtBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak-1) ], v[ getVertexNo(rtBreak) ], r );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak) ], v[ getVertexNo(rtBreak+1) ], r );
    }


  }//DRAW


 

  protected void drawSpan( int[] aE, Vertex2D vStart, Vertex2D vFinish, Raster r) {
      //System.out.println("IN TRIANGLE.DRAWSPAN......" );
    int pix;
    int aE0 = aE[0];    int aE1 = aE[1]; 
    //System.out.println( " aE0=" + aE0 +" aE1=" + aE1 ); 
        int x, y;
    y = (int)(vStart.gety()+0.5);
    int yFinish = (int)(vFinish.gety()+0.5);
    //System.out.println( " yStart=" + y +" yFinish=" + yFinish ); 

    while( y <= yFinish ) {
      int xFloor = (int)( -( (edge[aE0].getC()+(edge[aE0].getB() *(float)y)  )/ (edge[aE0].getA()) ) + 0.5 );
      int xCeil = (int)( -( (edge[aE1].getC()+(edge[aE1].getB() *(float)y)  )/ (edge[aE1].getA()) ) );
      //System.out.println( " y=" + y +"*************" ); 
      //System.out.println( "xFloor =" + xFloor +"xCeil =" + xCeil ); 
      for( x=xFloor; x<=xCeil; x++ ) {
	  //System.out.println( " x=" + x ); 
	pix = v[0].getargb(); //CHANGE THIS AFTER INTERPOLATION
	if( (x<120)&&(y>202)&&(y<212)){
	    System.out.println( "DRAWING THIS UNNECESARYT LINE" );    
	    System.out.println( " x=" + x +" y=" + y +  " pix=" + pix   );
	}
	r.setPixel( pix, x, y);
      }
      y++;
    }

  }//DRAWSPAN


    
    public boolean isPointInside(float x, float y) {
	int flag = 0;
	for( int i=0; i<3; i++ ) {
	    if( edge[i].evaluateAt(x,y)>=0 ) flag+=1;
	}
	if( flag==3 ) return true; 
	else return false;
    }

    // interface methods 
    public  void drawLine( Raster r ){;}
    public  void drawLine( Raster r, float[] alpha, float[] red,  float[] green,  float[] blue) {;}


} // CLASS TRIANGLE2D
