import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import Vertex2D;
import Triangle;
import ppmDecoder;

public class TriTest extends Applet {
    static int CHUNKSIZE = 100;
    Raster reference, raster;
    Drawable triList[];
    int triangles;
    Color background;
    Image screen;
    int state;
    int numDiffPixels;

    public void init( ) {
        // Read in the reference image
        ppmDecoder ppmDecoder = new ppmDecoder();
        screen = ppmDecoder.getImage(getDocumentBase(), getParameter("image"));
        reference = new Raster(screen);

        // Create a raster for rendering
        background = Color.white;
        raster = new Raster(reference.getWidth(), reference.getHeight());

        // Read in the triangle file
        triList = new Triangle[CHUNKSIZE];
        triangles = 0;
        InputStream is = null;
        try {
            is = new URL(getDocumentBase(), getParameter("datafile")).openStream();
            ReadInput(is);
            is.close();
        } catch (IOException e) {
            showStatus("Error reading "+getParameter("datafile"));
        }
        state = 0;
    }

    private void getNumDiffPixels() {
       int pixels = raster.pixel.length;
       numDiffPixels = 0;
       for (int i = 0; i < pixels; i++)
	  if(raster.pixel[i] != reference.pixel[i]) numDiffPixels++;
    }

    // parse a floting point number
    private float getNumber(StreamTokenizer st) throws IOException {
        st.nextToken();
        return (Float.valueOf(st.sval).floatValue());
    }

    // parse a packed integer in hex, octal, or decimal
    private int getColor(StreamTokenizer st) throws IOException {
        st.nextToken();
        int pix;
        if (st.sval.startsWith("0x") || st.sval.startsWith("0X")) {
	        pix = (int) Long.parseLong(st.sval.substring(2), 16);
	    } else
	    if (st.sval.startsWith("0") || st.sval.length() > 1) {
	        pix = (int) Long.parseLong(st.sval.substring(1), 8);
	    } else
	        pix = Integer.parseInt(st.sval);
        return pix;
    }

    // extend the triangle array if needed
    private void growList() {
        Drawable newList[] = new Drawable[triList.length+CHUNKSIZE];
        System.arraycopy(triList, 0, newList, 0, triList.length);
        triList = newList;
        CHUNKSIZE = (3*CHUNKSIZE)/2;
    }
    
    // define legal characters for our parser
    // The remainder of a line after a '#' is ignored
    // you might find this helpful for debugging
    private void configureParser(StreamTokenizer st) {
	    st.ordinaryChars(0, 255);
	    st.wordChars('a', 'f');
	    st.wordChars('A', 'F');
	    st.wordChars('0', '9');
	    st.wordChars('-', '-');
	    st.wordChars('.', '.');
	    st.wordChars('x', 'x');
	    st.wordChars('X', 'X');
	    st.whitespaceChars(' ', ' ');
	    st.whitespaceChars('\t', '\t');
	    st.whitespaceChars('\n', '\n');
	    st.whitespaceChars('\r', '\r');
    	st.eolIsSignificant(false);
    	st.commentChar('#');
    }

    // Scan the triangle file
    public void ReadInput(InputStream is) throws IOException {
        Vertex2D v1, v2, v3;
        int currentvert = 0;
        float x, y;
        int pix;

	    StreamTokenizer st = new StreamTokenizer(is);
	    configureParser(st);
        while (st.nextToken() != StreamTokenizer.TT_EOF) {
            st.pushBack();
            x = getNumber(st);
            y = getNumber(st);
            pix = getColor(st);
            v1 = new Vertex2D(x, y, pix);
            x = getNumber(st);
            y = getNumber(st);
            pix = getColor(st);
            v2 = new Vertex2D(x, y, pix);
            x = getNumber(st);
            y = getNumber(st);
            pix = getColor(st);
            v3 = new Vertex2D(x, y, pix);
            if (triangles == triList.length) growList();
            triList[triangles++] = new Triangle(v1, v2, v3);
	    }
	}
	
	
	String message[] = { "Reference", "Rendered", "Difference" };

    public void paint(Graphics g) {
        g.drawImage(screen, 0, 0, this);
        g.setColor(Color.black);
        g.drawString(message[state], 10, 20);
        if (state == 2) {
            double t = (badpixels*100.0)/pixels;
            g.drawString("Bad pixels = "+t+"%", 10, 36);
            g.drawString("Real Pixel Num: " + Rbadpixels + "/" + 
			 badpixels + "/" + numDiffPixels, 10, 52);
            if (badpixels > 0)
                t = ((double)pixelerror)/badpixels;
            else
                t = 0;
            g.drawString("Ave error  = "+t, 10, 68);
            g.drawString("Time  = "+time+" ms", 10, 84);
        }
    }

    public void update(Graphics g) {
        paint(g);
    }

    long time;
    int pixels;
    int badpixels;
    int Rbadpixels;
    int pixelerror;

    public boolean mouseDown(Event e, int x, int y) {
        if (state == 0) {
            raster.fill(background);
	    getNumDiffPixels();
            time = System.currentTimeMillis();
            for (int i = 0; i < triangles; i++)
                triList[i].Draw(raster);
            time = System.currentTimeMillis() - time;
            screen = raster.toImage( );
        } else
        if (state == 1) {
            int pixa, pixb, t, a, r, g, b;
            pixels = raster.pixel.length;
            badpixels = 0;
	    Rbadpixels = 0;
            pixelerror = 0;
	    int X,Y;
	    X = 0; Y = 0;
            for (int i = 0; i < pixels; i++, X++) {
	    if(X >= 400) {Y++; X -= 400; }
                pixa = raster.pixel[i];
                pixb = reference.pixel[i];
                if (pixa != pixb) {
		    a = Math.abs(((pixa & 0xFF000000) >> 24) - 
				 ((pixb & 0xFF000000) >> 24));
		    r = Math.abs(((pixa & 0x00FF0000) >> 16) - 
				 ((pixb & 0x00FF0000) >> 16));
		    g = Math.abs(((pixa & 0x0000FF00) >>  8) - 
				 ((pixb & 0x0000FF00) >>  8));
		    b = Math.abs(((pixa & 0x000000FF)) - 
				 ((pixb & 0x000000FF)));
                    badpixels += 1;
                    t = b + g + r;
                    pixelerror += t;

		    // if any error is > 1 in magnitude... count Really bad...
		    if (((a | r | g | b) & 0xFFFFFFFE) != 0) {
		       Rbadpixels++;
	       //System.out.println("Abs Diff A R G B " + a + "," + r + "," + 
	       //                   g + "," + b + "("+X+","+Y+")");
		    }

                    b = (255 - t);
                    if (b < 0) b = 0;
                    g = (t > 382) ? (637 - t) : (t - 127);
                    if (g < 0) g = 0;
                    r = (t - 510);
                    if (r < 0) r = 0;
                    raster.pixel[i] = ((((0xff00 + r) << 8) + g) << 8) + b;
                } else {
                    raster.pixel[i] = 0xffffffff;
                }
            }
            screen = raster.toImage( );
        } else
        if (state == 2) {
            screen = reference.toImage( );
        }
        state = (state + 1) % 3;
        repaint();
        return true;
    }

   public boolean mouseDrag(Event e, int x, int y) {   // not Java 1.1
      int pixa, pixb, i;
      int a, r, g, b;
      i = y * 400 + x;
       pixa = raster.pixel[i];
       pixb = reference.pixel[i];
       if (pixa != pixb) {
	   a = ((pixa & 0xFF000000) >> 24) - 
	       ((pixb & 0xFF000000) >> 24);
	   r = ((pixa & 0x00FF0000) >> 16) - 
	       ((pixb & 0x00FF0000) >> 16);
	   g = ((pixa & 0x0000FF00) >>  8) - 
	       ((pixb & 0x0000FF00) >>  8);
	   b = ((pixa & 0x000000FF)) - 
	       ((pixb & 0x000000FF));
           showStatus("X Y (" + x + "," + y + ") A R G B (" + a + "," + r + 
		      "," + g + "," + b + ") Hex " + 
		      Integer.toHexString(pixa)+"/"+
		      Integer.toHexString(pixb));

// if any error is > 1 in magnitude... count Really bad...
// if (((Math.abs(a)|Math.abs(r)|Math.abs(g)|Math.abs(b))&0xFFFFFFFE) != 0) {
//   System.out.println("Spot " + x + "," + y + " could be in:");
//   System.out.println("[ X Y (" + x + "," + y + ") A R G B (" + a + "," +
//   r + "," + g + "," + b + ") Hex " + Integer.toHexString(pixa)+"/"+
//   Integer.toHexString(pixb) + " ]");
//
// Old -- displayed on command line the triangles which might contain the point
//   for (i = 0; i < triangles; i++)
//   if(triList[i].isIn(x,y)) {
//      System.out.println(triList[i].toString());
//   }
// }

      } else {
      showStatus("X Y (" + x + "," + y + ")");
      }
      return true;
   }

}
