// (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 ArrayOutputEnvironment extends Component {
  static final int MAX_SIZE = OutputEnvironment.MAX_SIZE;

  MicroArray array;
  SimilarityMatrix matrix;
  ClusterMethod method;

  int gcount;
  int ecount;

  int gscale;
  int gextent;
  int gleftover;

  int escale;
  int eextent;
  int eleftover;

  float accum[][];


  // VISUAL

  static int colors[] = new int[256];
  static {
    for (int i = 0; i < 256; i++) {
      colors[i] = 0xff000000 | 
	((i < 128) ? ((127-i) << 17) : ((i-128) << 9));
    }
  }


  static float maxColor = Float.MIN_VALUE;
  static float minColor = Float.MAX_VALUE;

  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[];


  public ArrayOutputEnvironment(MicroArray array, SimilarityMatrix matrix) {
    this.array = array;
    this.matrix = matrix;

    gcount = matrix.gcount;
    ecount = matrix.ecount;

    if (gcount <= MAX_SIZE) {  // i don't think this 'if' is needed
      gscale = 1;

    } else {
      float scaling = (float)gcount / (float)MAX_SIZE;
      gscale = 1 << ((int) Math.ceil(Math.log(scaling) / Math.log(2)));
    }
    gextent = gcount / gscale;
    gleftover = gcount % gscale; // if some extra edge, extent moves beyond
    if (gleftover != 0) gextent++;

    escale = 1;
    eextent = ecount / escale;
    eleftover = ecount % escale;
    if (eleftover != 0) eextent++;

    accum = new float[gextent][eextent];

    // VISUAL

    pixels = new int[gextent*eextent];
    for (int i = 0; i < gextent*eextent; i++) pixels[i] = 0xff000000;
    source = new MemoryImageSource(eextent, gextent, pixels, 0, eextent);
    source.setAnimated(true);
    image = createImage(source);
  }


  public void update() {
    for (int j = 0; j < gextent; j++) {
      for (int i = 0; i < eextent; i++) {
	accum[j][i] = 0;
      }
    }

    int permute[] = method.permute;
    for (int n = 0; n < gextent-1; n++) {
      int nn = n * gscale;
      for (int m = 0; m < eextent-1; m++) {
	int mm = m * escale;

	int valid = 0;
	for (int j = 0; j < gscale; j++) {
	  for (int i = 0; i < escale; i++) {
	    float value = array.data[permute[nn + j]][mm + i];
	    if (value == value) {  // NaN
	      accum[n][m] += value;
	      valid++;
	    }
	  }
	}
	accum[n][m] /= valid;
      }
    }

    /*
    // 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 < gextent; j++) {
      for (int i = 0; i < eextent; i++) {
	pixels[index++] = color(accum[j][i]);
      }
    }
    source.newPixels();
    paint(this.getGraphics());
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    g.drawImage(image, 0, 0, eextent*2, gextent, null);
  }

  public Dimension preferredSize() {
    return new Dimension(eextent*2, gextent);
  }
}


