import java.awt.*;
import Raster;

public class ZRaster extends Raster {
    //zbuff will now be handled as a PctInt from 1 to 0
    //public final static int MAXZ = (1 << 16) - 1;
    public int zbuff[];
    private VertexList VX;       //point to the vertexList

    //Zraster knows how to rasterize a triangle
    //stuff for triangle rasterization:
    int row_pixel_index;  //index of a row in the array
    int top, left, right; //use to sort out which vertex is which
    Walker lWalker;      //Walk along left side
    Walker rWalker;      //Walk along right side
    Blender B;           //use to blend color (and z) along straight lines


    ///////////////////////// Constructors //////////////////////

    /**
     *  This constructor which takes no arguments
     *  allows for future extension.
     */
    public ZRaster()
    {
    }

    /**
     *  This constructor creates an uninitialized
     *  Raster Object of a given size (w x h).
     */
    public ZRaster(int w, int h)
    {
        super(w, h);
        zbuff = new int[w*h];
	rWalker = new Walker();
	lWalker = new Walker();
	B = new Blender();
    }

    /**
     *  This constructor creates an Raster initialized
     *  with the contents of an image.
     */
    public ZRaster(Image img)
    {
        super(img);
        zbuff = new int[this.size()];
    }

    /**
     *    Set all z values in the zbuffer
     *    to the maximum value
     */
    public final void resetz()
    {
        for (int i = 0; i < zbuff.length; i++)
            zbuff[i] = PctInt.ONE;
    }
    
    public final void setVertexList(VertexList VL) {
	VX = VL;
    }

    //////////////////////////////////////////////
    /// ZRaster TRIANGLE RASTERIZATION METHODS ///
    //////////////////////////////////////////////
    public void DrawTri(int v0,int v1,int v2) {
	sort_vx(v0,v1,v2);
	//init the walkers, and check if they've hit an end.
	if (lWalker.init(VX.get(top),VX.get(left))) {
	    //lW hit an endpoint
	    if (rWalker.init(VX.get(top),VX.get(right))) {  
		//rW has too, we must be done.
		return;
	    } else { 
		//lW should break_to rW's point
		lWalker.break_to(VX.get(right));
	    }
	} else {
	    if (rWalker.init(VX.get(top),VX.get(right))) {
		//rW has hit an end, and lW hasn't.
		// he should break_to lW's point
		rWalker.break_to(VX.get(left));
	    }
	}
	row_pixel_index = (FracInt.to_int(lWalker.getY())) * getWidth(); 
	fill_current_row();
	//step though rows:
	while (!step_row())
	    fill_current_row();
    }
    
    //Flill the current row (between lWalker and rWalker).
    private void fill_current_row() {
	//check that this row is really on the raster!
	if (row_pixel_index >= pixel.length || row_pixel_index < 0) {
	    return;
	}                       
	//get the x location of the end pixels
	int x1 = lWalker.getX();
	int x2 = rWalker.getX();
	
	if (x2 < x1) {
	    //System.err.println("wrong direction, correcting...");

	    int temp = x2;
	    x2 = x1; x1 = temp;
	}
	
	//get the Blenders of the endpoints
	B.init(lWalker.getBlend(), rWalker.getBlend(),x2 - x1);
	
	//step B back onto the screen (if needed)
	if (x1 < 0) {
	    if (x2 > 0) {
		B.step(-x1);
		x1 = 0; 
	    } else {
		return; //can't get back to the screen.
	    }
	}

	//step B to the next pixel.
	B.step(FracInt.ONE - ((x1 -1) & FracInt.FRACPART)); 	
	//set i to first pixel (plain int, round up)        
	int i = (x1 >>> FracInt.FRACBITS) + 1; 
	//set j to the first pixel after x2 (plain int, round up)
	int j = (x2 >>> FracInt.FRACBITS) + 1;   
	//Make sure we don't run off the edge.
	if (j > getWidth()) {
	  if (i < getWidth()) {
	    j = getWidth();
	  } else {
	    return;
	  }
	}
	
	i += row_pixel_index; //convert to pixel index
	j += row_pixel_index; //""
	
	//fill the pixels
	for (; i < j;) {
	    //check against zbuffer:
	    if (B.getZ() < zbuff[i]) {
		pixel[i] = B.getARGB();	//draw the pixel:
		zbuff[i] = B.getZ();    //update Zbuff
	    }
	    B.step();i++;
	}
    }
    
    //assign the appropriate vertex indeces to top right and left.
    //slow, but effective.
    private void sort_vx(int v0, int v1, int v2) {
	top = v0;
	if (VX.get(v1).x < VX.get(v2).x) {
	    left = v1;
	    right = v2;
	}else{ 
	    left = v2;
	    right = v1;
	}
	if (VX.get(v1).y < VX.get(top).y) {
	    top = v1;
	    if (VX.get(v0).x < VX.get(v2).x) {
		left = v0;
		right = v2;
	    }else{ 
		left = v2;
		right = v0;
	    }
	}
	if (VX.get(v2).y < VX.get(top).y) {
	    top = v2;
	    if (VX.get(v0).x < VX.get(v1).x) {
		left = v0;
		right = v1;
	    }else{ 
		left = v1;
		right = v0;
	    }
	}
    }
    
    //step everything down a row
    private boolean step_row() {
	//step the walkers, and check if they've hit an end.
	if (lWalker.step()) {
	    //lW hit an endpoint
	    if (rWalker.step()) {
		//rW has too, we must be done.
		return true;
	    } else {
		//lW should break_to rW's point
		lWalker.break_to(VX.get(right));
	    }
	} else {
	    if (rWalker.step()) {
		//rW has hit an end, and lW hasn't.
		// he should break_to lW's point
		rWalker.break_to(VX.get(left));
	    }
	}
	row_pixel_index += getWidth(); 
	return false;   
    }
}
