/*
 * Prime.java
 * Code: Jan Humble
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;


public class Prime extends Applet implements Runnable {

    int dY, dX;            // applet size
    TextArea resultArea;   // area in which to display results    
    InputPanel inputPanel; // panel in charge of input
    TextArea messageArea;  // area in which to display messages

    boolean calculating = false;

    int high = 1000;    
    int low  = 2;
    int nrPrimes = 0;
    
    FindPrime fp[];         // vector with multiple threads
    int nrThreads = 3;      // nr of threads
    int maxNrThreads = 10;  // limit of number of threads

    public void init(){
	
	// Get the drawing area of the applet 
	dY=getSize().height; dX=getSize().width;
	
	
	// Setting the layout
	setLayout(new BorderLayout());
	
	Label title = new Label("Multi-Thread Prime Search");
	title.setFont(new Font("Helvetica", Font.BOLD, 14));
	title.setBackground(Color.white);
	add("North", title);
	
	inputPanel = new InputPanel(this);
	add ("East", inputPanel);
	
	messageArea = new TextArea("", 2, 1);
	messageArea.setEditable(false);	
	messageArea.setBackground(Color.white);
	add("South", messageArea);
	
	resultArea = new TextArea("no results");
       	resultArea.setColumns(nrThreads);
	resultArea.setEditable(false);	
	resultArea.setBackground(Color.white);
	add("Center", resultArea);	


    }  
    

    /* Runnable run() overriden by threads' run() */
    public void run() {
	
    }
    
    public boolean createThreads() {
	fp = new FindPrime[nrThreads];
	
	/* partitioning interval length */
	int part = (int) ((high - low + 1) / (double) nrThreads); 
	
	for (int i = 0; i < nrThreads; i++) {
	    if (i + 1 >= nrThreads)   // last thread
		fp[i] = 
		    new FindPrime(low + part*i, high, this);
	    else
		fp[i] = 
		    new FindPrime(low + part*i, low + part*(i+1) - 1, this);
	    
	}
	
	return true;
		    
    }

    
    public void killThreads() {
	if (fp != null) {
	    for (int i = 0; i < nrThreads; i++)
		fp[i] = null;
	    fp = null;
	}
	
    }
    
    public void startThreads() {

	for (int i = 0; i < nrThreads; i++)
	    fp[i].start();
	
    }
    
    
    public boolean anyThreadActive() {

	if (fp != null)
	    for (int i = 0; i < nrThreads; i++)
		if (fp[i] != null && fp[i].active)
		    return true;
	return false;
	
    }
    
    public void displayMessage(String msg, boolean clear) {
	if (clear) {
	    messageArea.setText(msg);
	}
	else
	    messageArea.append(msg);	
    }
    

    /* Check if these values are OK to alter parameters */ 
    
    public boolean checkIntegrity(int low, int high, int nrThreads) {
	
	if (low < 2) {
	    displayMessage("Incorrect low value! 1 < low < high\n", true);
	    return false;
	}
	
	
	if (low > high) {
	    displayMessage("Incorrect value ranges given! 1 < low < high\n", true);
	    return false;
	}
	

   	if ((high - low + 1) < nrThreads) {
	    displayMessage("Too many threads for given interval!\n", true);
	    return false;
	}
	
	if (nrThreads > maxNrThreads) {
	    displayMessage("Too many threads ! Max " + maxNrThreads + "\n", true);
	    return false;
	}
	
	return true;
	
    }

    public void checkThreads() {
	if (!anyThreadActive())
	    {
		calculating = false;
		displayMessage("Calculating Finished!\n", false);
		killThreads();
		inputPanel.restore();
	    }
	
    }
    
    
    
    public void start() { 
	
	if (anyThreadActive()) stop();
	
	if (calculating && !anyThreadActive()){	
	    
	    if (createThreads()) {
		displayMessage("Calculating ...\n", true); 
		resultArea.setText(""); 
		startThreads();
	    }
	} 
    }
    

    public void stop() {
      
	killThreads();
	displayMessage("Threads stopped!\n", false);
	inputPanel.restore();
    
    }    
    
}


class FindPrime extends Thread {

    int nrPrimes;
    int low, high;
    Prime primeApplet;

    boolean active;

    FindPrime(int low, int high, Prime primeApplet) {
	
	this.primeApplet = primeApplet;
	
	this.low = low;
	this.high = high;
	this.active = true;
	
    }

    public void run() {
	
	boolean primality = true;
	int i;

	for (i = low; i <= high && primeApplet.calculating; i++) {	
	    primality = true;
	    for (int j = 2; j <= Math.sqrt(i); j++) {
		
		if ((i % j) == 0)
		    
		    primality = false;		
	    }

	    // prime number found
	    if (primality == true) {
		primeApplet.resultArea.append(String.valueOf(i) + "\n");
		nrPrimes++;
		primeApplet.nrPrimes++;
		primeApplet.inputPanel.update();
		
	    }
	    yield();  // after each prime found let other threads calculate
	    
	} 
	
	if (i > high)  // finished completely
	
	    primeApplet.displayMessage(getName() + 
				       " done! Found: " +
				       String.valueOf(nrPrimes) + " primes " +
				       "in interval [" + low + ", " + high + "]\n",
				       false);
	else
	    primeApplet.displayMessage(getName() + 
				       " stopped! Found: " +
				       String.valueOf(nrPrimes) + " primes " +
				       "in interval [" + low + ", " + i + "]\n",
				       false);
	active = false;
	primeApplet.checkThreads();
    }
    
    
   
    
    
}





class InputPanel extends Panel implements ActionListener {

    Prime primeApplet;
    
    Button findButton, stopButton, clearButton;
    TextField lowField, highField, nrPrimesField, nrThreadsField;

    

    InputPanel(Prime primeApplet) {
	
	this.primeApplet = primeApplet;
   	
	this.setLayout(new GridLayout(11,1));
	this.setBackground(Color.white);
	
	findButton = new Button("Find");	

	stopButton = new Button("Stop");	

	clearButton = new Button("Clear");	
       
	restore();
	
	lowField = new TextField("2");
	highField = new TextField("1000");
	nrPrimesField = new TextField("0");
	nrPrimesField.setEditable(false);
	nrThreadsField = new TextField("3");
    
	
	findButton.addActionListener(this);	
	stopButton.addActionListener(this);
	clearButton.addActionListener(this);
       
	add(findButton);
	add(stopButton);
	add(clearButton);
	add(new Label("Low"));
	add(lowField);	
	add(new Label("High (> low)"));
	add(highField);	
	add(new Label("Nr Threads"));
	add(nrThreadsField);
	add(new Label("Nr Primes"));
	add(nrPrimesField);

	update();

    } 
    
    
    
    public void actionPerformed(ActionEvent ev) {
	if (ev.getActionCommand().equals("Find")) {

	    int low = 	Integer.valueOf(lowField.getText()).intValue();
	    int high = 	Integer.valueOf(highField.getText()).intValue();
	    int nrThreads = Integer.valueOf(nrThreadsField.getText()).intValue();
	    
	    if (primeApplet.checkIntegrity(low, high, nrThreads)) {
	
		/* Make sure all threads are dead before changing parameters */
		primeApplet.killThreads();
		
		primeApplet.nrPrimes = 0;
		primeApplet.high = high;
		primeApplet.low = low;
		primeApplet.nrThreads = nrThreads;
		primeApplet.calculating = true;
		
		primeApplet.start();	
		findButton.setEnabled(false);
		stopButton.setEnabled(true);
	    }
	    
	}
	else if (ev.getActionCommand().equals("Stop")) {
	    primeApplet.calculating = false;
	
	    restore();

	}
	else if (ev.getActionCommand().equals("Clear")) {
	    primeApplet.resultArea.setText("No results");
	    primeApplet.displayMessage("", true);
	}
	
	
    }
    
    public void update() {
	nrPrimesField.setText(String.valueOf(primeApplet.nrPrimes));	
	
    }

    public void restore() {
	findButton.setEnabled(true);
	stopButton.setEnabled(false);
    }
    
}



