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

public class InterpolatedTriangle3D extends Triangle3D implements Drawable {
  public final static int FRACBITS = 12 ;   
  protected boolean isUniform;
  //protected double scale; 
  protected float alpha[];
  protected float red[]; 
  protected float green[];
  protected float blue[];
  //protected float zEqn[];


  ///////  CONSTRUCTORS /////////////////////////////  
  
  // constructor using three Vertex3D 
  public InterpolatedTriangle3D( Vertex3D v0,  Vertex3D v1,  Vertex3D v2 ) {
    super(v0, v1, v2);

    //check if all vertices are the same color
    isUniform = (v[0].getColor() == v[1].getColor()) && (v[0].getColor() == v[2].getColor() );
    //Scale is always non zero and positive. This zero
    //value indicates that it has not been computed yet
    //scale = -1;
    alpha = new float[3];     red = new float[3];   
    green = new float[3];     blue = new float[3];
    //zEqn = new float[3];    
    //  this.interpolatedTriangleSetup( );
  }	
  
  /////////  METHODS  ////////////////////////////
 
    /** SETUP HAS TO BE DONE WHENEVER MAJOR FUNCTIONS LIKE CLIPPING ETC ARE CALLED
    THIS ASSUMES THAT THE VERTICES ARE IN W=1 FORMAT. HENCE, DE-HOMOGENIZE BEFORE 
    USING SETUP */
  protected void interpolatedTriangleSetup( ) {
//       System.out.println("IN INTERPOLATEDTRIANGLESETP(RASTER)" );
    int t0 = v[0].getColor();
    int t1 = v[1].getColor();
    int t2 = v[2].getColor();
 //    System.out.println("COLOR INTERPOL");
    //System.out.println("BLUE");
    super.planeEqn(blue, t0 & 255, t1 & 255, t2 & 255);
    t0 >>= 8;   t1 >>= 8;   t2 >>= 8;
    //System.out.println("GREEN");
    super.planeEqn(green, t0 & 255, t1 & 255, t2 & 255);
    t0 >>= 8;   t1 >>= 8;   t2 >>= 8;
    //System.out.println("RED");
    super.planeEqn(red, t0 & 255, t1 & 255, t2 & 255);
    t0 >>= 8;   t1 >>= 8;   t2 >>= 8;
    //System.out.println("ALPHA");
    super.planeEqn(alpha, t0 & 255, t1 & 255, t2 & 255);
    //System.out.println( " green0=" + green[0] +" green1=" +green[1]+ "green[2] =" + green[2] );         

//     float z[] = new float[3];
//     for( int i=0; i<3; i++ ) {
// 	if( isNotZero(v[i].getCoord(3)) ) {
// 	    z[i] = v[i].getCoord(2)/v[i].getCoord(3);
// 	}
// 	//TK:DO SOMETHING ABOUT THIS
// 	if(isPosZero(v[i].getCoord(3)) ) {
// 	    System.out.println( "WARNING: InterpolatedTriangle3D.interpolatedTriangleSetup( ).isPosZero(v[i].getCoord(3) is accessed" );         
   
// 	}
// 	if(isNegZero(v[i].getCoord(3)) ) {
// 	    System.out.println( "WARNING: InterpolatedTriangle3D.interpolatedTriangleSetup( ).isNegZero(v[i].getCoord(3) is accessed" );   
// 	}
//     }
//     planeEqn(zEqn, z[0], z[1], z[2]);
    return;
  }
  
  //*******************************************************************//


  public void draw( Raster r) {
//           System.out.println("IN INTERPOLATEDTRIANGLE.DRAW(RASTER)" );
      /** TRIANGLE HAS TO BE SET UP BEFORE ANY MAJOR FUNCTIONS LIKE DRAWING, CLIPPING */
    this.interpolatedTriangleSetup( );

//       if( isUniform ) {
// 	  System.out.println("IN ISUNIFORM => SUPER.DRAW" );	
// 	  super.draw(r);
// 	  return;
//       }
    //      if( isPointInside(133.f,250.f) )
    //System.out.println("THIS POINT IN TRI NO" );    

    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, Vertex3D vStart, Vertex3D vFinish, Raster r) {
   //    System.out.println("IN INTERPOLATEDTRIANGLE.DRAWSPAN......" );
    int pix;
    int h = r.getHeight(); 
    int w = r.getWidth();
//     System.out.println( " w=" + w +" h=" + h ); 

    int aE0 = aE[0];    int aE1 = aE[1]; 
 //    System.out.println( " ae0=" + aE0 +" aE1=" + aE1 );
    int x, y;
    y = (int)(vStart.getCoord(1)+0.5);
    int yFinish = (int)(vFinish.getCoord(1)+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  ); 
	if (x >= 0 && y >= 0 && x < w && y < h){
	  pix = setColor(x,y);
	  //System.out.println( " x=" + x +" y=" + y +  " pix=" + pix   );
	  r.setPixel( pix, x, y);
	}
      }
      y++;
    }

  }//DRAWSPAN


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

  //####################################################################################//

  public void draw( Raster r, Buffer buf) {
//           System.out.println("IN INTERPOLATEDTRIANGLE.DRAW(RASTER)" );
      /** TRIANGLE HAS TO BE SET UP BEFORE ANY MAJOR FUNCTIONS LIKE DRAWING, CLIPPING */
    this.interpolatedTriangleSetup( );

//       if( isUniform ) {
// 	  System.out.println("IN ISUNIFORM => SUPER.DRAW" );	
// 	  super.draw(r);
// 	  return;
//       }
    //      if( isPointInside(133.f,250.f) )
    //System.out.println("THIS POINT IN TRI NO" );    

    if( noseNegY >= 0){
      getActiveEdges();
      drawSpan( activeEdge, v[ getVertexNo(noseNegY) ], v[ getVertexNo(noseNegY-1) ], r, buf );
    }
    if( nosePosY >= 0){
      getActiveEdges(); 
      drawSpan( activeEdge, v[ getVertexNo(nosePosY+1) ], v[ getVertexNo(nosePosY) ], r, buf ); 
    }
    if( ltBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak+1) ], v[ getVertexNo(ltBreak) ], r, buf );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak) ], v[ getVertexNo(ltBreak-1) ], r, buf );
    }
    if( rtBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak-1) ], v[ getVertexNo(rtBreak) ], r, buf );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak) ], v[ getVertexNo(rtBreak+1) ], r, buf );
    }

  }//DRAW



  protected void drawSpan( int[] aE, Vertex3D vStart, Vertex3D vFinish, Raster r, Buffer buf) {
   //    System.out.println("IN INTERPOLATEDTRIANGLE.DRAWSPAN......" );
    int pix;
    float z;
    int h = r.getHeight(); 
    int w = r.getWidth();
//     System.out.println( " w=" + w +" h=" + h ); 

    int aE0 = aE[0];    int aE1 = aE[1]; 
 //    System.out.println( " ae0=" + aE0 +" aE1=" + aE1 );
    int x, y;
    y = (int)(vStart.getCoord(1)+0.5);
    int yFinish = (int)(vFinish.getCoord(1)+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  ); 
	z = this.getZBuf(x, y);
	if (x >= 0 && y >= 0 && x < w && y < h){
	    if( buf.getZBuf(x, y) >= z ) {
		pix = setColor(x,y);
		//System.out.println( " x=" + x +" y=" + y +  " pix=" + pix   );
		r.setPixel( pix, x, y);
		buf.setZBuf( z, x, y );
	    }
	}
      }
      y++;
    }
  }//DRAWSPAN

//   protected float getZBuf( int x, int y) {
//       return (zEqn[0]*x + zEqn[1]*y + zEqn[2] );
//     }

  //####################################################################################//

//   protected void planeEqn(float eqn[], int p0, int p1, int p2){
//       //System.out.println("IN PLANE EQN" );
//     float Ap, Bp, Cp;
//     if (scale <= 0) {
//       scale = (double)1. / ((double) twiceArea);
//     }
//     double sp0 = scale * p0;
//     double sp1 = scale * p1;
//     double sp2 = scale * p2;

//     Ap = (float)(edge[2].getA()*sp2 + edge[0].getA()*sp0 + edge[1].getA()*sp1);
//     Bp = (float)(edge[2].getB()*sp2 + edge[0].getB()*sp0 + edge[1].getB()*sp1);
//     Cp = (float)(edge[2].getC()*sp2 + edge[0].getC()*sp0 + edge[1].getC()*sp1);
//     eqn[0] = Ap;
//     eqn[1] = Bp;
//     eqn[2] =  Cp;
//     return;
//   }//PLANEEQN
//   protected void planeEqn(float eqn[], float p0, float p1, float p2){
//       //System.out.println("IN PLANE EQN" );
//     float Ap, Bp, Cp;
//     if (scale <= 0) {
//       scale = (double)1. / ((double) twiceArea);
//     }
//     double sp0 = scale * p0;
//     double sp1 = scale * p1;
//     double sp2 = scale * p2;

//     Ap = (float)(edge[2].getA()*sp2 + edge[0].getA()*sp0 + edge[1].getA()*sp1);
//     Bp = (float)(edge[2].getB()*sp2 + edge[0].getB()*sp0 + edge[1].getB()*sp1);
//     Cp = (float)(edge[2].getC()*sp2 + edge[0].getC()*sp0 + edge[1].getC()*sp1);
//     eqn[0] = Ap;
//     eqn[1] = Bp;
//     eqn[2] =  Cp;
//     return;
//   }//PLANEEQN


  protected int setColor(int x, int y) {
    int ca = Math.round(alpha[0]*x + alpha[1]*y + alpha[2] );
    int cr = Math.round(red[0]*x + red[1]*y + red[2] );
    int cg = Math.round(green[0]*x + green[1]*y + green[2] );
    int cb = Math.round(blue[0]*x + blue[1]*y + blue[2] );
    return( (ca<<24)|(cr<<16)|(cg<<8)|(cb) );
  }
  protected int setColor(float x, float y) {
    int ca = Math.round(alpha[0]*x + alpha[1]*y + alpha[2] );
    int cr = Math.round(red[0]*x + red[1]*y + red[2] );
    int cg = Math.round(green[0]*x + green[1]*y + green[2] );
    int cb = Math.round(blue[0]*x + blue[1]*y + blue[2] );
    return( (ca<<24)|(cr<<16)|(cg<<8)|(cb) );
  }



  //********************************************************************//

  public int clipTri3D( InterpolatedTriangle3D[] clipList, int clipTris, Plane3D[] H, int nPlanes, int CLIPFAC ) {
    System.out.println("IN INTERPLATED.CLIPTRI3D" );
    /** TRIANGLE HAS TO BE SET UP BEFORE ANY MAJOR FUNCTIONS LIKE DRAWING, CLIPPING */
    this.interpolatedTriangleSetup( );
    int nClipVtx = 3;
    Vertex3D[] inVtx = new Vertex3D[CLIPFAC]; 
    inVtx = this.v;
    Vertex3D[] clipVtxList = new Vertex3D[CLIPFAC]; 

    for( int i=0; i<nPlanes; i++ ) {
	if( nClipVtx > 0) { 
	    Vertex3D[] outVtx = new Vertex3D[CLIPFAC];
	    System.out.println("CLIPPING AGAINS PLANE = " + i );  
	    for( int j=0; j<nClipVtx; j++) {
		System.out.println("inVtx["+j+"]= " + j ); 	
		inVtx[j].print();
	    }
	    nClipVtx =  suthHodgPolyClip( inVtx,  outVtx, nClipVtx,  H[i] ) ;
	    for( int j=0; j<nClipVtx; j++) {
		System.out.println("outVtx["+j+"]= " + j ); 	
		outVtx[j].print();
	    }
	    inVtx = outVtx;
	    System.out.println("no of clipped vertices  = " + nClipVtx ); 
	    if( i==(nPlanes-1) ) clipVtxList = outVtx;
	}
	else break;
    }
    if( nClipVtx >= 3) {
	triangulate( clipList, clipTris, nClipVtx, clipVtxList  );
	return (nClipVtx-2);//NUMBE OF TRIANGLES CREATED
    } else{
	return 0;
    }
  }

  protected void triangulate(  InterpolatedTriangle3D[] outTri, int outStart, int nVtx, Vertex3D[] vtx ) {
      System.out.println("IN INTERPLATED.TRIANGULATE" );
    //TRIANGLES CREATED = NVTX -2
    for(int i=0; i< (nVtx-2); i++ ) {
      outTri[outStart+i] = new InterpolatedTriangle3D( vtx[0], vtx[i+1], vtx[i+2] );
    }
    return;
  } 
  

  protected int suthHodgPolyClip( Vertex3D[] in,  Vertex3D[] out, int inLength, Plane3D H ) {
      System.out.println("IN SUTHHODPOLYCLIP" );
//       for( int jj=0; jj<inLength; jj++) {
// 	  System.out.println("Printing in["+jj+"]" ); 	
// 	  in[jj].print();
//       }
    Vertex3D s, p;
    int outLength = 0;

    if( inLength>0) {
	s = in[ inLength-1];
	for( int j=0; j<inLength; j++ ) {
	    System.out.println(" ANALYZING LINE SEGMENT ="+j ); 
	    p = in[j];
	    // 	System.out.println("Printing vertex p" );
	    // 	p.print();
	    if( isInside(p, H) ) {
		System.out.println("p is inside H" );
		if( isInside(s, H) ) {
		    System.out.println("s is inside H" );
		    outLength = output( p, outLength, out );
		}
		else {
		    System.out.println("s is 0utside H" );
		    Vertex3D i = new Vertex3D();
		    intersect(s, p, H, i);
		    // 		System.out.println("Printing intersection vertex i" );
		    // 		i.print();
		    outLength = output( i, outLength, out );
		    outLength = output( p, outLength, out );
		}
	    }
	    else {
		System.out.println("p is outside H" );
		if( isInside(s, H) ) {
		    System.out.println("s is inside H" );
		    Vertex3D i = new Vertex3D();
		    intersect( s, p, H, i );
		    outLength = output( i, outLength, out );
		}
	    }
	    // 	for( int jj=0; jj<inLength; jj++) {
	    // 	    System.out.println("4-Printing in["+jj+"]" ); 	
	    // 	    in[jj].print();
	    // 	}
	    // 	for( int jj=0; jj<outLength; jj++) {
	    // 	    System.out.println("out["+jj+"]= " + jj ); 	
	    // 	    out[jj].print();
	    // 	}
	    s=p;
	    
	}//FOR J
    }
    return outLength;   
  } //SUTHHODGPOLYCLIP
    

  protected int output( Vertex3D v, int outLength, Vertex3D[] out ) {
    out[outLength] = new Vertex3D( v.getCoord(0), v.getCoord(1), v.getCoord(2), v.getCoord(3), v.getColor() );
    outLength++;
    return outLength;
  }
    

  /** THIS NEEDS TO BE POLISHED FOR CASES NOT GIVEN BY BELOW
       Assumes that H.v0 and H.v1 are in different halfplanes of H */
  protected void intersect( Vertex3D v0, Vertex3D v1, Plane3D H, Vertex3D res ) {
      System.out.println(" IN INTERSECT"  );
      this.printColorInterpol();
    float normDist0 = H.dot(v0);
    float normDist1 = H.dot(v1);
    float fac = normDist0 /(normDist0 - normDist1 );
//     System.out.println("normDist0 " + normDist0 );
//     System.out.println("normDist " + normDist1 );
//     System.out.println("fac " + fac  );

    if(( (normDist0<0)&&(normDist1>0) )||( (normDist0>0)&&(normDist1<0) ) ) {
	res.setCoord(0, (v0.getCoord(0) + fac*( v1.getCoord(0) - v0.getCoord(0) ) ) );
	res.setCoord(1, (v0.getCoord(1) + fac*( v1.getCoord(1) - v0.getCoord(1) ) ) );
	res.setCoord(2, (v0.getCoord(2) + fac*( v1.getCoord(2) - v0.getCoord(2) ) ) );
	res.setCoord(3, 1 );
	System.out.println("res.getCoord0="+ res.getCoord(0)+"res.getCoord1="+ res.getCoord(1) );
	System.out.println("res.color=" + this.setColor(res.getCoord(0),res.getCoord(1))  );	
	// SINCE RES.W = 1, RES.X AND RES.Y CAN BE DIRECTLY INPUT TO THIS.SETCOLOR(X,Y)
	res.setColor( this.setColor( res.getCoord(0), res.getCoord(1) ) );
    } else {
      System.err.println("ERROR: InterplatedTriangle3D.intersect(): line and plane do not intersect OR line is on plane" );
    }
  } //INTERSECT

  
    /** =0 DOESNOT MAKE SENSE FOR FLOATS */
    protected boolean isInside( Vertex3D v, Plane3D H ) {
    if((H.dot(v)>0)||(!isNotZero(H.dot(v)))) return true; else return false;
    }


  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@//

  protected boolean isNotZero( float f ) {
    if ( Math.abs(f) >= (1>>FRACBITS) ) return true; else return false;
  }
  protected boolean isPosZero( float f ) {
    if ( (f>0)&&( Math.abs(f) < (1>>FRACBITS)) ) return true;
    else return false;
  }
  protected boolean isNegZero( float f ) {
    if ( (f<0)&&( Math.abs(f) < (1>>FRACBITS)) ) return true;
    else return false;
  }


    public void printColorInterpol() {
	System.out.println( " alpha0="+ alpha[0]+" alpha1="+ alpha[1]+" alpha2="+ alpha[2] );
	System.out.println( " red0="+ red[0]+" red1="+ red[1]+" red2="+ red[2] );
	System.out.println( " green0="+ green[0]+" green1="+ green[1]+" green2="+ green[2] );
	System.out.println( " blue0="+ blue[0]+" blue1="+ blue[1]+" blue2="+ blue[2] );
    }


} // CLASS INTERPOLATEDTRIANGLE3D
