
// MandelWorker.java
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* MandelSpace generates tuples of the form:
      ("RENDER", <start line> <no. of lines>)

   A worker gets one of these at random and 
   generates lines of pixels for the image.
   These are added to the space as tuples of the form:
       ("PIC", <worker id>, <start line> <no. of lines>,
                <2D pixel array>)

   The worker keeps doing this until there are no more
   RENDER tuples to process.

   Once the image is completed, MandelSpace issues a DIE
   tuple, which causes all the workers to terminate.

  Usage:
    > compile.bat MandelWorker.java

    Part of the application made up of:
       * MandelSpace.java
       * MandelWorker.java
*/

import org.jspace.*;


public class MandelWorker implements Runnable
{
  private static final int NUM_ITERS = 10000;
                                // was 1000000

  // region for which Mandelbrot is being computed
  private double x1 = -2.25;  // (x1,y1) to (x2, y2) 
  private double x2 = 3.0;
  private double y1 = -1.8;
  private double y2 = 3.3;

  private Space space;
  private PrintAreas pas;
  private int id, width, height;


  public MandelWorker(int id,  int width, int height,
                         PrintAreas pas, Space space)
  {
    this.space = space;
    this.pas = pas;
    this.id = id;
    pas.setTitle(id, "W-"+id);
    this.width = width; this.height = height;
  }


  public void run()
  {
    int numRenders = 0;
    try {
      while(true) {
        Object[] res = space.queryp(new ActualField("DIE"));
        if (res != null) {
          pas.println(id, "Terminating after " + numRenders +
                            " renders");
          break;
        }

        // select a RENDER task
        Object[] rendObj = 
            space.getp( new ActualField("RENDER"),
                  new FormalField(Integer.class),  // start line
                  new FormalField(Integer.class)); // no. lines

        if (rendObj != null) {
          int i = (int)rendObj[1];
          int numLines = (int)rendObj[2];
	      
          int[][] pixels = calcMandelbrot(i, numLines);
	      
          space.put("PIC", id, i, numLines, pixels);
          pas.println(id, "Calculated " + i +  ", " + numLines );
          numRenders++;
        }
        // SpaceUtils.pause(10);  
      }
    }
    catch (InterruptedException e) 
    {  System.out.println("W-" + id + " error: " + e);  }
  } /* run */




  private int[][] calcMandelbrot(int start, int numLines)
  /* calculate the Mandelbrot pixel values starting
     from line == start, and for numLines
     This is the same function as used in SeqMandelbrot.java
  */
  {
    double x, y, xx, a;

    int end = start + numLines;
    double da = x2/width;
    double db = y2/height;

    double b = y1;

    int[][] pixels = new int[width][numLines];

    for (int i = 0; i < start; i++)
      b = b + db;

    int k = 0;
    for (int i = start; i < end; i++, k++) {
      a = x1;
      for (int j = 0; j < width; j++) {
        int n = 0;
        x = 0.0;
        y = 0.0;
        while ((n < NUM_ITERS) && ((x*x)+(y*y) < 4.0)) {
          xx = x*x - y*y + a;
          y = 2*x*y + b;
          x = xx;
          n++;
        }
        pixels[j][k] = n;
        a = a + da;
      }
      b = b + db;
    }
    return pixels;
  } /* calcMandelbrot */


}  // end of MandelWorker class
