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;
  
  public void init( ) {
    // Read in the reference image
    ppmDecoder ppmDecoder = new ppmDecoder();	

    //System.err.println(getParameter("image"));
    //System.err.println(getDocumentBase());

    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;
  }
  
  // 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);
      if (badpixels > 0)
	t = ((double)pixelerror)/badpixels;
      else
	t = 0;
      g.drawString("Ave error  = "+t, 10, 52);
      g.drawString("Time  = "+time+" ms", 10, 68);
    }
  }
  
  public void update(Graphics g) {
    paint(g);
  }
  
  long time;
  int pixels;
  int badpixels;
  int pixelerror;
  
  public boolean mouseDown(Event e, int x, int y) {
    if (state == 0) {
      raster.fill(background);
      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;
	pixels = raster.pixel.length;
	badpixels = 0;
	pixelerror = 0;
	for (int i = 0; i < pixels; i++) {
	  pixa = raster.pixel[i];
	  pixb = reference.pixel[i];
	  if (pixa != pixb) {
	    badpixels += 1;
	    t = Math.abs((pixa & 255) - (pixb & 255));
	    t += Math.abs(((pixa >> 8) & 255) - ((pixb >> 8) & 255));
	    t += Math.abs(((pixa >> 16) & 255) - ((pixb >> 16) & 255));
	    pixelerror += t;
	    int b = (255 - t);
	    if (b < 0) b = 0;
	    int g = (t > 382) ? (637 - t) : (t - 127);
	    if (g < 0) g = 0;
	    int 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 mouseMove(Event e, int x, int y)
  {
    showStatus(x + "," + y);
    return true;
  }
}






