// (c) 2000 Benjamin Fry, MIT Media Laboratory, fry@media.mit.edu
// Aesthetics + Computation Group, Massachussetts Institute of Technology


import java.awt.*;
import java.awt.image.*;


public class OutputEnvironment extends Component {
  static final int MAX_SIZE = 600;
  int scale;

  MicroArray array;
  SimilarityMatrix matrix;
  ClusterMethod method;

  int gcount;

  int extent;
  int leftover;
  float accum[][];


  // VISUAL

  static int colors[];
  static {
    colors = new int[256];
    for (int i = 0; i < 256; i++) {
      colors[i] = 0xff000000 | (i << 16) | (i << 8) | i;
    }
  }

  static float maxColor = 1;
  static float minColor = 0;

  static final int color(float v) {
    if (v > maxColor) maxColor = v;
    if (v < minColor) minColor = v;
    return colors[(int) (((v - minColor) / (maxColor - minColor)) * 255f)];
  }

  Image image;
  MemoryImageSource source;
  int pixels[];

  // don't update to the screen every single time
  int updateCalls = -1;
  int updateIncrement = -1;


  // ARRAY

  ArrayOutputEnvironment aoutput;


  public OutputEnvironment(MicroArray array, SimilarityMatrix matrix) {
    this.array = array;
    this.matrix = matrix;

    gcount = matrix.gcount;

    if (gcount <= MAX_SIZE) {  // i don't think this 'if' is needed
      scale = 1;
      
    } else {
      float scaling = (float)gcount / (float)MAX_SIZE;
      scale = 1 << ((int) Math.ceil(Math.log(scaling) / Math.log(2)));
    }
    extent = gcount / scale;
    leftover = gcount % scale;
    if (leftover != 0) extent++;

    // how many elements in last piece (when size is < scale)
    accum = new float[extent][extent];

    // VISUAL

    pixels = new int[extent*extent];
    for (int i = 0; i < extent*extent; i++) pixels[i] = 0xff000000;
    source = new MemoryImageSource(extent, extent, pixels, 0, extent);
    source.setAnimated(true);
    image = createImage(source);

    updateIncrement = (gcount < 512) ? 1 : gcount / 16;
    if (updateIncrement == 0) updateIncrement = 1;
  }


  public void update() {
    updateCalls++;
    if ((updateCalls % updateIncrement) != 0) return;
    if (aoutput != null) aoutput.update();

    for (int j = 0; j < extent; j++) {
      for (int i = 0; i < extent; i++) {
	accum[i][j] = 0;
      }
    }

    // get current permutation from the clustering method class
    int permute[] = method.permute;
    for (int n = 0; n < extent-1; n++) {
      int nn = n * scale;
      for (int m = 0; m < extent-1; m++) {
	int mm = m * scale;

	//int valid = 0;
	for (int j = 0; j < scale; j++) {
	  for (int i = 0; i < scale; i++) {
	    //accum[mm+i][nn+j] += matrix.get(mm, nn);
	    accum[m][n] += matrix.get(permute[mm+i], permute[nn+j]);
	  }
	}
	accum[m][n] /= scale*scale;
      }
    }

    /*
    // bottom row and righthand column
    int n = extent-1;
    int nn = n * scale;
    for (int m = 0; m < extent-1; m++) {
      int mm = m * scale;

      for (int j = 0; j < scale; j++) {
	for (int i = 0; i < leftover; i++) {
	  System.out.println(m + " " + n + "  " + 
			     mm + " " + nn + " " + 
			     i + " " + j);
	  accum[m][n] += matrix.get(permute[mm+i], permute[nn+j]);
	  accum[n][m] += matrix.get(permute[nn+j], permute[mm+i]);
	}
      }
      accum[m][n] /= scale*leftover;
      accum[n][m] /= scale*leftover;
    }

    // bottom right corner
    int m = extent-1;
    int mm = m * scale;
    for (int j = 0; j < leftover; j++) {
      for (int i = 0; i < leftover; i++) {
	accum[m][m] += matrix.get(permute[mm+i], permute[mm+j]);
      }
    }
    accum[m][m] /= leftover*leftover;
    */

    int index = 0;
    for (int j = 0; j < extent; j++) {
      for (int i = 0; i < extent; i++) {
	pixels[index++] = color(accum[i][j]);
      }
    }
    source.newPixels();
    paint(this.getGraphics());

    // writing a series of tiff images to make a movie for class
    //((ClusterMethodCAST)method).writeMovieFrame();
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    g.drawImage(image, 0, 0, null);
  }

  public Dimension preferredSize() {
    return new Dimension(extent, extent);
  }
}


