import java.awt.*;
import java.awt.event.*;
import cs3.*;
import whuang.*;
import TodoData;
//import Complex;

public class Fractal extends Frame
{
  private int nmax;
  protected Panel infobar;
  protected FracCanvas mainpic;
  private Color[] colormap;
  protected Label message1 = new Label("Click to zoom in, Ctrl-click to zoom out");
  protected Label message2 = new Label("Mandlebrot set");
  protected double mincre = -2.0;
  protected double maxcre = 2.0;
  protected double mincim = -2.0;
  protected double maxcim = 2.0;

  private double trunc(double innn) {
    return(((double)(int)(innn*100000.0))/100000.0); }

  private class MyMouseListener implements MouseListener {
    public void mouseClicked(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {

      FracCanvas temp = (FracCanvas)e.getComponent();
      Dimension mysize = temp.getSize();
      double xrat = ((double)e.getX())/((double)mysize.width);
      double yrat = ((double)e.getY())/((double)mysize.height);
      double newabswidth, newabsheight, newxcenter, newycenter;

      if (e.isControlDown()) {
        // zooming out.
        newabswidth = maxcre-mincre;
        newabsheight = maxcim-mincim;
        newxcenter = mincre+newabswidth*xrat;
        newycenter = maxcim-newabsheight*yrat;

        message1.setText("Mouse pressed with Ctrl, zooming out...");
      } else {
        // zooming in.
        newabswidth = (maxcre-mincre)/4.0;
        newabsheight = (maxcim-mincim)/4.0;
        newxcenter = mincre+newabswidth*4.0*xrat;
        newycenter = maxcim-newabsheight*4.0*yrat;

        message1.setText("Mouse pressed, zooming in...");
      }
      mincre = newxcenter - newabswidth;
      mincim = newycenter - newabsheight;
      maxcre = newxcenter + newabswidth;
      maxcim = newycenter + newabsheight;
      message2.setText("("+trunc(mincre)+","+trunc(mincim)
        +")("+trunc(mincre)+","+trunc(mincim)+")");
      infobar.repaint();
      mainpic.repaint();
    }
  }

  private class MyListener implements ActionListener {
    public void actionPerformed(ActionEvent evt) {
      // reset the screen.
      mincre = -2.0;
      mincim = -2.0;
      maxcre = 2.0;
      maxcim = 2.0;
      message1.setText("Resetting!");
      message2.setText("("+trunc(mincre)+","+trunc(mincim)
        +")("+trunc(mincre)+","+trunc(mincim)+")");
      infobar.repaint();
      mainpic.repaint();
    }
  }

  public Fractal()
  {
    super("Da Fractal");   
    MyListener listen = new MyListener();
    MyMouseListener mlisten = new MyMouseListener();

    System.out.println("This implements homework 7.");
    System.out.println("Version 2");
    System.out.println("");
    System.out.println("Not as inefficient -- it does it in 12x12 chunks.");
    System.out.println("");
    System.out.println("Click anywhere to zoom in 50%.");
    System.out.println("Ctrl-click anywhere to zoom out 50%.");
    System.out.println("");
    System.out.println("Hit the reset button to reset (duh).");

    infobar = new Panel();
    GridLayout gd = new GridLayout(3,1);
    infobar.setLayout(gd);
    Button button = new Button("Reset");
    button.addActionListener(listen);
    infobar.add(button);
    infobar.add(message1);
    infobar.add(message2);

    setLayout(new BorderLayout());
    add("North", infobar);  
    mainpic = new FracCanvas();
    mainpic.addMouseListener(mlisten);
    add("Center",mainpic);  
    pack();   

    nmax = 60;
    colormap = new Color[nmax + 1];
    for (int lcv=0;lcv<=60;lcv++) {
      float r = (lcv - 30);
      r = (r<0)? -r-10: r-10;
      if (r<0) r=0; if (r>10) r=10;
      float g = (lcv - 20);
      g = (g<0)? g+20: -g+20;
      if (g<0) g=0; if (g>10) g=10;
      float b = (lcv - 40);
      b = (b<0)? b+20: -b+20;
      if (b<0) b=0; if (b>10) b=10;
      
      colormap[lcv] = new Color(r/10f,g/10f,b/10f);
    }
    
    // handle window close events:
    addWindowListener(new WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        System.out.println("Goodbye!");
        System.exit(0);
      }
    });

    setVisible(true);                 // show window
  }

  public static void main(String[] args)
  {
    new Fractal();
  }

  private class FracCanvas extends Canvas implements Runnable {
    private Thread painter = new Thread(this);
    private SlaveDriver[] sdlist = new SlaveDriver[10];
    protected TodoList tdlist = new TodoList();
    protected TodoList anslist = new TodoList();

    public Dimension getPreferredSize() { return new Dimension(200, 250);    }
    public FracCanvas() { 
      System.out.println("Initializing 10 slave drivers ... ");
      for (int lcv=0;lcv<10;lcv++) {
        sdlist[lcv] = new SlaveDriver(lcv);
        sdlist[lcv].start();
        System.out.println("Driver "+lcv+" initialized. ");
      }
    }
    public void paint(Graphics g) {
      painter.stop();
      painter = new Thread(this);
      painter.setPriority(Thread.MIN_PRIORITY);
      painter.start();
    }

    private class SlaveDriver extends RemoteProcess {
      int myindex;
      public SlaveDriver(int index) {
        super();
        myindex = index;
      }
      public void run() {
        RemoteObject myslave = new RemoteObject("WWSlaveObject3");
        Integer answer;
        while (true) {
          TodoData td = (TodoData)tdlist.get();
          /*
          System.out.println("Driver "+myindex
            +" recieved data, starting at pixel ("
            +td.xmin+","+td.ymin+")"
            +" and ending at pixel ("
            +td.xmax+","+td.ymax+")");
          */
          myslave.invoke("setrect",
            new Integer(td.xmin),new Integer(td.xmax),
            new Integer(td.ymin),new Integer(td.ymax));
          PixelBuffer result = (PixelBuffer)myslave.invoke("rectcalc",
            new Double(td.remin),new Double(td.reinc),
            new Double(td.immax),new Double(td.iminc));
          result.draw(getGraphics());
          //System.out.println("Driver "+myindex+" finished with data.");
        }
      }
    }

    public void run() {
      Graphics g = getGraphics();
      Dimension mysize = getSize();

      double wth = maxcre - mincre;
      double hht = maxcim - mincim;
      double check = maxcre;

      int ut = 12;

      double incre = wth/(double)mysize.width;
      double incim = hht/(double)mysize.height;
      double increut = incre*(double)ut;
      double incimut = incim*(double)ut;

      double xcval;
      double ycval;

      // put stuff in stack.
      tdlist.trashall();

      int space = 5;

//      for (int xoff = 0; xoff < space; xoff ++) {
//        for (int yoff = 0; yoff < space; yoff ++) {
      int xoff;
      for (int xyoff = 2*space-2; xyoff >=0; xyoff --) {
        for (int yoff = ((xyoff>=space)?(xyoff-space+1):0);
                  yoff < ((xyoff>=space)?space:(xyoff+1)); yoff ++) {

          xoff = xyoff - yoff;

          xcval = mincre+increut*xoff;

          for (int xpos = xoff*ut; xpos < mysize.width; xpos += ut*space) {
            ycval = maxcim-incimut*yoff;
            for (int ypos = yoff*ut; ypos < mysize.width; ypos += ut*space) {
    
              TodoData temp = new TodoData();
              temp.setints(xpos,xpos+ut,ypos,ypos+ut);
              temp.setdubs(xcval,incre,ycval,incim);
              tdlist.put(temp); 

              ycval -= incimut*space;
            }
            xcval += increut*space;
          }

        } 
      }

    }

  }

}
