import edu.ucdavis.rj.*;

public class Worker {

    private OpImpl start;
    public OpImpl compute;
    public OpInni toprow = OpInni.newOpInni();
    public OpInni bottomrow = OpInni.newOpInni();

    // (former resource parameters)
    int id, N, PR, S;
    double xl, xt, xr, xb;

    public Worker(int xid, int xN, int xPR, int xS,
                  double xl, double xt, double xr, double xb)
    {
        this.id = xid;
	this.N = xN;
	this.PR = xPR;
	this.S = xS;
	this.xl = xl;
	this.xt = xt;
	this.xr = xr;
	this.xb = xb;
            start.call();  // start the client thread
	    // note: use call since grid needs to be initialized
	    // before compute runs!
	    // could just move start code here or send to start
	    // but have compute wait until start finishes.
    }

    private Double grid[][][];
    private int cur = 0, nxt = 1;
    private double diff;

    {
	try {
            start = new OpMethod() {
		    public void codeBlock(Invocation inv) {

			grid = new Double[2][S+2][N+2];

			// initialize grid
			for( int g = 0; g <= 1; g++ ) {
			    for( int r = 0; r <= S+1; r++ ) {
				grid[g][r][0] = xl;
				grid[g][r][N+1] = xr;
				for( int c = 1; c <= N; c++ ) {
				    grid[g][r][c] = 0.0;
				}
			    }
			}
			////System.out.println("here"+iters);
			if (id==1) {// top for process 1
			    for( int c = 0; c <= N+1; c++ ) {
				grid[cur][0][c] = grid[nxt][0][c] = xt;
			    }
			}
			if (id==PR) {// bottom for process PR
			    for( int c = 0; c <= N+1; c++ ) {
				grid[cur][S+1][c] = grid[nxt][S+1][c] = xb;
			    }
			}
			////System.out.println("here"+iters);
		    }
		};
	    compute = new OpMethod() {
		    public void codeBlock(Invocation inv) {
			RemoteRefs up = (RemoteRefs)(inv.getParam(0));
			RemoteRefs down = (RemoteRefs)(inv.getParam(1));
			OpProxy terminate = (OpProxy)(inv.getParam(2));
			OpProxy gather = (OpProxy)(inv.getParam(3));
			////        System.out.println("compute id="+id);

			OpProxy ub = up.get("bottomrow");
			OpProxy dt = down.get("toprow");

			while( true ) {
			    // compute new values for grid points
			    diff = 0.0;
			    for( int r = 1; r <= S; r++ ) {
				for( int c = 1; c <= N; c++ ) {
				    grid[nxt][r][c] =
					( grid[cur][r-1][c] 
					  +grid[cur][r][c-1] 
					  +grid[cur][r+1][c] 
					  +grid[cur][r][c+1]
					  ) / 4.0;
				    diff = Math.max(diff,
						    Math.abs(grid[nxt][r][c]-grid[cur][r][c]));
				}
			    }
			    ////            System.out.println("worker id="+id+
			    ////	                       " done iteration");

			    // replace old values by new ones, and
			    // exchange top and bottom rows with neighbors
			    int tmp;
			    tmp = cur; cur = nxt; nxt = tmp;
			    // 2000-03-06 need next 2 "if" here since noop doesn't work now
			    if (id != 1 ) {
				try {
				    /// send up.bottomrow(grid[cur][1]);
				    ub.send(new Invocation((Object)grid[cur][1]));
				    ////                System.out.println("worker SENT1 id="+id);
				} catch (Exception e) {
				    System.err.println("dt.send");
				    e.printStackTrace();
				}
			    }
			    ////            System.out.println("worker A id="+id);
			    if (id != PR ) {
				/// send down.toprow(grid[cur][S]);
				try {
				    dt.send(new Invocation((Object)grid[cur][S]));
				////                System.out.println("worker SENT2 id="+id);
				} catch (Exception e) {
				    System.err.println("dt.send");
				    e.printStackTrace();
				}
			    }
			    ////            System.out.println("worker B id="+id);
			    if (id != 1 ) {
				////                System.out.println("worker RECV1 id="+id);
				Invocation rinv = toprow.receive();
				//////////////				grid[cur][0] = (Double [])(rinv.getParam(0));
				Double [] dd = (Double [])(rinv.getParam(0));
				for (int c = 0; c < N+2; c++) {
				    grid[cur][0][c] = dd[c];
				}
			    }
			    ////            System.out.println("worker C id="+id);
			    if (id != PR ) {
				////                System.out.println("worker RECV2 id="+id);
				Invocation rinv = bottomrow.receive();
				///////				grid[cur][S+1]= (Double [])(rinv.getParam(0));
				Double [] dd = (Double [])(rinv.getParam(0));
				for (int c = 0; c < N+2; c++) {
				    grid[cur][S+1][c] = dd[c];
				}
			    }

			    // check for termination
			    ////            System.out.println("worker id="+id+
			    ////	                       " checking terminate, diff="+diff);
			    try {
				Invocation rinv = terminate.call(new Invocation(diff));
				boolean enough = (Boolean)(rinv.getReturnValue());
				if (enough) {
				    break;
				}
			    } catch (Exception e) {
				System.err.println("terminate(diff)");
				e.printStackTrace();
			    }
			}
			try {
			    gather.send(new Invocation(id, grid[cur]));
			} catch (Exception e) {
			    System.err.println("gather");
			    e.printStackTrace();
			}
		    }
		    
		};
	} catch (Exception e) {
	    System.err.println("init opmethods");
	    e.printStackTrace();
	}
    }
}



