/*----------------------------------------------------------------------+
|   Title:  ColorPlaneWindow.java                                       |
|                                                                       |
|   Author: David E. Joyce                                              |
|           Department of Mathematics and Computer Science              |
|           Clark University                                            |
|           Worcester, MA 01610-1477                                    |
|           U.S.A.                                                      |                                                                       |
|           http://aleph0.clarku.edu/~djoyce/                           |
|                                                                       |
|   Date:   January, 2003.                                              |
+----------------------------------------------------------------------*/

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class ColorPlaneWindow extends Canvas
  implements MouseListener {

  private ColorPlane p;
  private Complex center;
  private double radius;
  private double scale;
  private boolean changed = true;

  public ColorPlaneWindow (ColorPlane p, Complex center,
        double radius, double scale) {
    this.p = p;
    this.center = new Complex(center);
    this.radius = radius;
    this.scale = scale;
    addMouseListener(this);
  }

  public ColorPlaneWindow (ColorPlane p, double center_x, double center_y,
        double radius, double scale) {
    this.p = p;
    this.center = new Complex(center_x,center_y);
    this.radius = radius;
    this.scale = scale;
    addMouseListener(this);
  }

  public void setPlane (ColorPlane p) {
    this.p = p;
    changed = true;
 }

  public void setCenter (Complex center) {
    this.center = new Complex(center);
    changed = true;
  }

  public void setCenter (double center_x, double center_y) {
    center = new Complex(center_x,center_y);
    changed = true;
  }

  public void setRadius (double radius) {
    this.radius = radius;
    changed = true;
  }

  public void setScale (double scale) {
    this.scale = scale;
  }

  public void setChanged () {
    changed = true;
  }

  public ColorPlane getPlane () { return p; }
  public Complex getCenter() { return center; }
  public double getRadius() { return radius; }
  public double getScale() { return scale; }

  // convert screen coordinates to a complex number
  public Complex convert(int x, int y) {
    int width = getSize().width;
    int height = getSize().height;
    double minWH = Math.min(width,height);
    double factor = radius/minWH;
    Complex temp = new Complex(2.0*x-width, height-2.0*y);
    return temp.times(factor).plus(center);
  }

  // The purpose of the extra graphics buffer is not to reduce flickering,
  // but to keep from recomputing when the window comes back into view.
  // People like to see the image develop.
  private Image offscreen;
  private Dimension offscreensize;
  private Graphics offgraphics;

  public void update(Graphics g) {
    Dimension d = getSize();
    if ((offscreen == null) || (d.width != offscreensize.width)
                            || (d.height != offscreensize.height)) {
      offscreen = createImage(d.width, d.height);
      offscreensize = d;
      offgraphics = offscreen.getGraphics();
      offgraphics.setFont(g.getFont());
      changed = true;
    }
    if (changed) {
      createGraph(g,offgraphics);
      changed = false;
    } else
      g.drawImage(offscreen, 0, 0, null);
  }

  private void createGraph(Graphics g, Graphics h) {
    int width = getSize().width;
    int height = getSize().height;
    Color[][] c = new Color[width][height];
    double minWH = Math.min(width,height);
    double scale = 2.0*radius/minWH;
    double low_x = center.x - width*scale/2.0;
    double low_y = center.y - height*scale/2.0;

    int d = 1;
    while (d<width || d<height)
      d <<= 1; // double d
    do {
      for (int i=0; i<width; i += d) {
        for (int j=0; j<height; j += d) {
          if (i%(d<<1) == 0 && j%(d<<1) == 0 && (i!=0 || j!=0)) continue;
          Color newColor = p.valueAt(low_x + i*scale, low_y + j*scale);
          if (c[i][j] == null || !newColor.equals(c[i][j])) {
            g.setColor(newColor); h.setColor(newColor);
            g.fillRect(i,height-j-d,d,d); h.fillRect(i,height-j-d,d,d);
            for (int s=0; s<d; ++s) if (i+s < width)
              for (int t=0; t<d; ++t) if (j+t < height)
                c[i+s][j+t] = newColor;
          } // if
        } // for j
      } // for i
      d /= 2;
    } while (d > 0);
  } // update

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

  // mouse methods
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  public void mouseReleased(MouseEvent e) {}
  public void mousePressed(MouseEvent e) {}

  public void mouseClicked(MouseEvent e) {
    center = convert(e.getX(),e.getY());
    radius /= scale;
    changed = true;
    repaint();
  } // mouseClicked

} // ColorPlaneWindow
