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

public class Fractal extends Frame
{
  protected static int slavenum;
  protected Panel infobar;
  protected FracCanvas mainpic;
  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 3.1");
    System.out.println("");
    System.out.println("Attempts divide and conquer.");
    System.out.println("Put a number on the command line to");
    System.out.println("specify number of servants (10 is default).");
    System.out.println("");
    System.out.println("(It's still slow since there are 60 colors.)");
    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();   

    // 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)
  {
    slavenum = 10;
    new Fractal();
    if (args.length == 1) {
      try {
        slavenum = Integer.valueOf(args[0]).intValue();
        if (slavenum < 1) slavenum = 1;
        if (slavenum > 16) slavenum = 16;
      }
      catch (NumberFormatException e) {
        slavenum = 10;
      }
    } else {
      slavenum = 10;
    }
  }

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

    public Dimension getPreferredSize() { return new Dimension(200, 250);    }
    public FracCanvas() { 
      supercode = 0;
      sdlist = new SlaveDriver[slavenum];
      System.out.println("Initializing "+slavenum+" slave drivers ... ");
      for (int lcv=0;lcv<slavenum;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("WWSlaveObject7");
        Integer answer;
        while (true) {
          TodoData td = (TodoData)tdlist.get();
          if (td.session == supercode) {
            myslave.invoke("setrect",
              new Integer(td.xmin),new Integer(td.xmax),
              new Integer(td.ymin),new Integer(td.ymax));
            PixelBuffer result = (PixelBuffer)myslave.invoke("rectcalc2",
              new Double(td.remin),new Double(td.reinc),
              new Double(td.immax),new Double(td.iminc));
            if (td.session == supercode) {
              result.draw(getGraphics());
              Boolean allsame = (Boolean)myslave.invoke("allsamep");
              if (!allsame.booleanValue()) {
                // chop it up and put it back.
                int centerx = (td.xmin+td.xmax-1)/2;
                double centerre = td.remin+td.reinc*(double)(centerx-td.xmin);
                int centery = (td.ymin+td.ymax-1)/2;
                double centerim = td.immax-td.iminc*(double)(centery-td.ymin);
                TodoData minmin = new TodoData();
                minmin.session=td.session;
                minmin.setints(td.xmin+1,centerx,td.ymin+1,centery);
                minmin.setdubs(td.remin+td.reinc,td.reinc,
                               td.immax-td.iminc,td.iminc);
                TodoData minmax = new TodoData();
                minmax.session=td.session;
                minmax.setints(td.xmin+1,centerx,centery,td.ymax-1);
                minmax.setdubs(td.remin+td.reinc,td.reinc,
                               centerim,td.iminc);
                TodoData maxmin = new TodoData();
                maxmin.session=td.session;
                maxmin.setints(centerx,td.xmax-1,td.ymin+1,centery);
                maxmin.setdubs(centerre,td.reinc,
                               td.immax-td.iminc,td.iminc);
                TodoData maxmax = new TodoData();
                maxmax.session=td.session;
                maxmax.setints(centerx,td.xmax-1,centery,td.ymax-1);
                maxmax.setdubs(centerre,td.reinc,
                               centerim,td.iminc);
                tdlist.put(minmin);
                tdlist.put(minmax);
                tdlist.put(maxmax);
                tdlist.put(maxmin);
              }
            }
            //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;

      double incre = wth/(double)mysize.width;
      double incim = hht/(double)mysize.height;

      // put stuff in stack.
      tdlist.trashall();
      supercode++;
      if (supercode == 10) supercode = 0;

      // Find the x-axis.
      int ypos = 0;
      double ycval = maxcim;
      while ((ycval > 0) && (ypos <= mysize.width)) {
        ypos++;
        ycval -= incim;
      }
      if (ypos > mysize.width) {
        // just do the whole thing.
        TodoData temp = new TodoData();
        temp.session = supercode;
        temp.setints(0,mysize.width+1,0,mysize.height+1);
        temp.setdubs(mincre,incre,maxcim,incim);
        tdlist.put(temp); 
      } else {
        // split into two.
        TodoData temp1 = new TodoData();
        temp1.session = supercode;
        temp1.setints(0,mysize.width+1,0,ypos);
        temp1.setdubs(mincre,incre,maxcim,incim);
        tdlist.put(temp1); 
        TodoData temp2 = new TodoData();
        temp2.session = supercode;
        temp2.setints(0,mysize.width+1,ypos,mysize.height+1);
        temp2.setdubs(mincre,incre,ycval,incim);
        tdlist.put(temp2); 
      }

    }

  }

}
