/*  SwingPrim.java : testing Swing-GUI with threads and prime numbers
    Copyright (C) 2005  Stefan Ziegler

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

import javax.swing.*;
import javax.swing.table.*;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class SwingPrim
{
	// table for prime numbers and counter, one per thread
	class PrimTableModel extends AbstractTableModel
	{
		private String		column_names[] =
						{"Last found", "Count"};
		private Vector		prim_number_list = new Vector();
		private Vector		prim_count_list = new Vector();

		public int getColumnCount()		// number of columns
		{
			return column_names.length;
		}

		public int getRowCount()		// number of rows
		{
			return Math.max(prim_number_list.size(),
					prim_count_list.size());
		}

		public String getColumnName(int column)	// names of columns
		{
			return column_names[column];
		}

		// returns value at row/column
		public Object getValueAt(int row, int column)
		{
			Integer	value = new Integer(0);

			// select prime numbers or count
			if (column == 0)
			{	// does the rows exists in the table?
				if (row < prim_number_list.size())
					value = (Integer) prim_number_list.
							elementAt(row);
			}
			if (column == 1)
			{	// does the rows exists in the table?
				if (row < prim_count_list.size())
					value = (Integer) prim_count_list.
							elementAt(row);
			}
			return value;
		}

		// class of each cell in table, for output rendering
		public Class getColumnClass(int column)
		{
			Integer	value = new Integer(0);

			return value.getClass();
		}

		// sets the value in row/column
		public void setValueAt(Object value, int row, int column)
		{
			boolean	rows_insert = false;

			// select prime numbers or count
			if (column == 0)
			{	// add row if needed
				if (row >= prim_number_list.size())
					rows_insert = true;
				if (rows_insert)
					prim_number_list.setSize(row + 1);
				// set value
				prim_number_list.setElementAt(value, row);
			}
			if (column == 1)
			{	// add row if needed
				if (row >= prim_count_list.size())
					rows_insert = true;
				if (rows_insert)
					prim_count_list.setSize(row + 1);
				// set value
				prim_count_list.setElementAt(value, row);
			}
			// redraw displayed table
			if (rows_insert) fireTableStructureChanged();
			fireTableCellUpdated(row, column);
		}

		// help method for new prime number
		public void setPrimNumber(int value, int row)
		{
			setValueAt(new Integer(value), row, 0);
		}

		// help method for new prime count
		public void setPrimCount(int value, int row)
		{
			setValueAt(new Integer(value), row, 1);
		}
	}

	private int		thread_count = 0;	// number of threads
	private JFrame		frame = null;		// window
	private JButton		startbutton = null, stopbutton = null;// buttons
	private LinkedList	thread_list = new LinkedList();	// thread list
	private PrimTableModel	prim_table = new PrimTableModel();// primes

	// class for each thread
	class PrimThread extends Thread
	{
		// maximum count of (saved) prime numbers per thread
		static final int	array_size = 100000;
		private int		my_thread_num = 0; // thread index
		// initial values for prime number index and prime number
		private int		prim_index = 0, prim_number = 1;
		// array for all prime numbers in thread
		private int[]		prim_array = new int[array_size];
		// to stop thread
		volatile boolean	doRun = true;

		public PrimThread(int thread_num)	// constructor;
		{
			my_thread_num = thread_num;
			for(int i = 0; i < array_size; i++)
				prim_array[i] = 0;
		}

		// test if prim_number is really prime
		private boolean checkPrim(int prim_number)
		{
			boolean	stop = false, is_prim = true;
			int	i = 1;
			double	prim_root = 0;

			// only test until square root of prime number
			prim_root = Math.ceil(Math.sqrt(prim_number));
			while(!stop)
			{
				stop = (prim_root < prim_array[i]);
				// no more prime numbers for testing
				if (!stop) stop = (prim_array[i] == 0);
				if (!stop)
				{	// calculate rest of division (Modulo)
					is_prim = (prim_number % prim_array[i]
							!= 0);
					/* System.out.println(prim_array[i]);*/
					stop = !is_prim;
				}
				i++;	// next prime number index to test
			}
			/* System.out.println("-------------"); */
			return is_prim;
		}

		// save found prime number in table, increment index,
		// update displayed swing table with number and count
		public void insertPrim(int prim_number)
		{
			if (prim_index < array_size)
				prim_array[prim_index] = prim_number;
			prim_index++;
			prim_table.setPrimCount(prim_index, my_thread_num - 1);
			prim_table.setPrimNumber(prim_array[prim_index - 1],
							my_thread_num - 1);
		}

		// thread main method
		public void run()
		{	// first prime numbers for initial values
			insertPrim(2);
			insertPrim(3);
			insertPrim(5);
			insertPrim(7);
			insertPrim(11);
			prim_number = 11;
			while (doRun)
			{	// until array full
				if (prim_index < array_size)
				{	// test (..3, ..7, ..9, ..1)
					prim_number += 2;
					if (checkPrim(prim_number))
						insertPrim(prim_number);
					// don't need to test '..5'
					prim_number += 4;
					if (checkPrim(prim_number))
						insertPrim(prim_number);
					prim_number += 2;
					if (checkPrim(prim_number))
						insertPrim(prim_number);
					prim_number += 2;
					if (checkPrim(prim_number))
						insertPrim(prim_number);
				}
				else doRun = false;
				try
				{	// sleep to let time for other threads
					Thread.sleep(50);
				}
				catch (InterruptedException e)
				{
				}
			}
		}

		// thread finish desired
		public void pleaseStop()
		{
			doRun = false;
		}
	}

	// actions behind the buttons: start one thread
	class ThreadStartAction extends AbstractAction
	{
		protected ThreadStartAction()
		{
		}

		public void actionPerformed(ActionEvent e)
		{	// increment count, create, start, into list
			thread_count++;
			Thread thread_handle = new PrimThread(thread_count);
			thread_handle.start();
			thread_list.add(thread_handle);
		}
	}

	// actions behind the buttons: stop one thread (first in list)
	class ThreadStopAction extends AbstractAction
	{
		protected ThreadStopAction()
		{
		}

		public void actionPerformed(ActionEvent e)
		{
			if (thread_list.size() > 0)
			{	// get thread handle, stop, remove from list,
				// decrement count
				Thread thread_handle = null;
				thread_handle = (Thread) thread_list.getFirst();
				thread_handle.stop();
				thread_list.removeFirst();
				thread_count--;
			}
		}
	}

	public SwingPrim(GraphicsConfiguration gc)
	{	// create frame (main window)
		frame = new JFrame(gc);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setTitle("SwingPrim");	// title
		frame.setSize(300, 150);	// initial size
		frame.getContentPane().setLayout(new BorderLayout());
		JLabel label = new JLabel("  Thread  ");
		// create buttons with actions
		startbutton = new JButton("Start");
		startbutton.addActionListener(new ThreadStartAction());
		stopbutton = new JButton("Stop");
		stopbutton.addActionListener(new ThreadStopAction());
		// add buttons to frame
		Box button_box = new Box(BoxLayout.X_AXIS);
		button_box.add(label);
		button_box.add(startbutton);
		button_box.add(stopbutton);
		frame.getContentPane().add(button_box, BorderLayout.NORTH);
		// create table with scrolling, add to frame, 
		JTable table = new JTable(prim_table);
		JScrollPane scrolling = new JScrollPane(table);
		table.setPreferredScrollableViewportSize(
			new Dimension(200, 70));
		frame.getContentPane().add(scrolling, BorderLayout.SOUTH);
		/* frame.pack(); */
		frame.show();
	}

	public static void main(String[] args)
	{	// create window and start program
		SwingPrim swingprim = new SwingPrim(GraphicsEnvironment.
						getLocalGraphicsEnvironment().
						getDefaultScreenDevice().
						getDefaultConfiguration());

	}
}

