/*
 * Class MdpPuzzleGenerator
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.Calendar;

/** <dt><b>Direct Known Subclasses:</b></dt><dd><a href="MdpPuzzleGenerator.Canvas.html">MdpPuzzleGenerator.Canvas</a><br><br></dd>
 * MdpPuzzleGenerator generates MDP-type problem input files to be used by the PosgInputFileParser object
 * @author Mark Gruman -- UMass RBR Lab <br>
 * Computer Science Department <br>
 * University of Massachusetts Amherst <br>
 * @version 1.0 <br>
 * Created on June 28, 2004 <br><br>
 */
public class MdpPuzzleGenerator  
{
	private Canvas m_canvas;
	private String file_name;
	private BufferedWriter m_log_writer;
	private BufferedReader m_log_reader;
	private int m_current_screen;
	private PosgInputFileParser m_parser;

	private String[] temp_actions; 
	private String[] temp_observs; 
	private	String[] temp_states;
	private int temp_agents;

	private String[] m_actions;
	private String[] m_observs;

	/** The main method creates a new instance of the MdpPuzzleGenerator class. */
	public static void main(String[] args) 
	{
		MdpPuzzleGenerator pg = new MdpPuzzleGenerator();
		pg.getCanvas().start();
	}

	/** Constructor creates a new canvas */
	private MdpPuzzleGenerator()
	{
		m_canvas = new Canvas();
	}

	/** This method returns the associated PosgInputFileParser object 
	 * @return the corresponding PosgInputFileParser
	 */
	public PosgInputFileParser getParser()
	{
		return m_parser;
	}

	/** This method returns the associated Canvas object 
	 * @return the corresponding Canvas
	 */
	private Canvas getCanvas()
	{
		return m_canvas;
	}

	/** This method writes the contents of a JTextField to the corresponding text file
	 * @param c a set of given components in which the output data is entailed
	 * @param i an index number corresponding to the component that comtains the output text
	 * @param s a textual prefix to be written out with its corresponding output text
	 */
	private void outputTextField(Component[] c, int i, String s)
	{
		try {
			m_log_writer.write(s + ((JTextField)c[i+1]).getText());
			m_log_writer.newLine();
		}
		catch (IOException ioe) { }
	}

	/** This method parses the user's list of given parameters into an array of Strings
	 * @param param a flag differentiating between actions and observations
	 * @param values the list of given parameters as provided by the user
	 */
	private void setupParams(String param, String values)
	{
		String symbol;
		if (param.startsWith("ALL A"))
			symbol = "a";
		else
			symbol = "o";

		String res = "-- ALL ";
		try {
		    int num_params = Integer.parseInt(values);
		    for (int i=0; i<num_params; i++) 
			res += symbol + i + " ";

		} catch (NumberFormatException nfe) {
			res += values;
		}

		if (symbol.equals("a"))
		    temp_actions = res.split("\\s");
		else
		    temp_observs = res.split("\\s");

		return;
	}

	/** This method generates all permutations of user-selected actions and observations */
	private void setupParamCombos()
	{
		m_actions = new String[m_parser.getActions()+2];
		m_actions[0] = "--";
		m_actions[1] = "ALL";
		for (int i=2; i<m_actions.length; i++) 
			m_actions[i] = m_parser.getParamName(i-2, true);

		m_observs = new String[m_parser.getObs()+2];
		m_observs[0] = "--";
		m_observs[1] = "ALL";
		for (int i=2; i<m_observs.length; i++) 
			m_observs[i] = m_parser.getParamName(i-2, false);
	}

	/** This method parses the user's list of given states into an array of Strings
	 * @param values the list of given states as provided by the user
	 */
	private void setupStates(String[] values)
	{
		temp_states = new String[values.length+2];
		temp_states[0] = "--";
		temp_states[1] = "ALL";
		for (int i=0; i<values.length; i++)
			temp_states[i+2] = values[i] + " ";
	}

	/** This method processes all data entered by users in GUI screen 1 and transfers the data into a
	 * newly-created and properly-formatted corresponding text file
	 * @return true if new file was succesfully created and all required data was properly formatted,
	 * false otherwise
	 */
	private boolean processScreenOne()
	{
		String s = "";
		Component[] c = getCanvas().getScreen(1).getComponents();
		try {
		  for (int i=0; i<c.length-2; i+=2) {
		    try {
		      s = ((JLabel)c[i]).getText();

		      if (s.equals("Title: ")) {

/*			file_name = "JAVA/MDP Puzzle Generator/" + ((JTextField)c[i+1]).getText();
			  if (file_name.equals("JAVA/MDP Puzzle Generator/"))
			    return false;
*/
			file_name = ((JTextField)c[i+1]).getText();
			  if (file_name.equals(""))
			    return false;
			File file = new File(file_name);

			m_log_writer = new BufferedWriter(new FileWriter(file));
			m_log_writer.write("# File: " + file_name);
			m_log_writer.newLine();
			m_log_writer.write("# Created on: " + Calendar.getInstance().getTime());
			m_log_writer.newLine();
			m_log_writer.newLine();

		      } else if (s.equals("Values: ")) {
			if (!((JComboBox)c[i+1]).getSelectedItem().equals(" -- ")) {
			  m_log_writer.write(s + ((JComboBox)c[i+1]).getSelectedItem());
			  m_log_writer.newLine();
		      } else
			return false;

		      } else if (s.equals("Agents: ") || s.equals("Start: ")) {
			if (!((JTextField)c[i+1]).getText().equals(""))
			  outputTextField(c, i, s);
			else
			  continue;

		      } else  if (s.equals("ALL Actions: ") || s.equals("ALL Observtns: ")) {
			if (!((JTextField)c[i+1]).getText().equals(""))
			  setupParams(s, ((JTextField)c[i+1]).getText());
			else
			  return false;
			
		      } else  {
			if (!((JTextField)c[i+1]).getText().equals(""))
			  outputTextField(c, i, s);
			else
			  return false;
		      }

		    } catch (Exception e) {
		      System.out.println(e + " in " + s);
		      System.out.println("Data not formatted properly in screen 1");
		      System.exit(1);
		    }
		  }
		    m_log_writer.flush();
		    m_parser = new PosgInputFileParser(file_name);

		} catch (IOException e) {
		  System.out.println(e);
		  System.exit(1);
		}
		return true;
	}

	/** This method generates properly-formatted text of the selected actions and observations of each agent
	 * @param c a set of given components in which the output data is entailed
	 * @param params a matrix that holds all specified actions and observations of all agents
	 * @param index the starting index of components to be processed 
	 * @param title a flag differentiating between actions and observations
	 * @param v_list a generic list of ALL actions or observations
	 * @return the appropriately-formatted text to be written to a corresponding output file
	 */
	private String generateParamOutput(Component[] c, Object[][] params, int index, String title, String[] v_list)
	{
		String values = "\r\n" + title;
		for (int i=index; i<c.length-3; i+=3) {
			params[i] = ((JList)((JScrollPane)c[i]).getViewport().getView()).getSelectedValues();
			for (int j=0; j<params[i].length; j++) {
				if (((String)params[i][j]).equals("--"))
					return null;
				else if (((String)params[i][j]).equals("ALL")) {
					for (int k=2; k<v_list.length; k++)
						values += v_list[k] + " ";
					break;
				}
				else
					values += (String)params[i][j] + " ";
			}
			values += "\r\n";
		}
		return values;
	}

	/** This method processes all data entered by users in GUI screen 2 and transfers the data into a
	 * properly-formatted corresponding text file
	 * @return true if all required data was properly formatted, false otherwise
	 */
	private boolean processScreenTwo()
	{
		try {
			Component[] c = getCanvas().getScreen(2).getComponents();
			Object[][] temp = new Object[c.length-3][];

			String values1 = generateParamOutput(c, temp, 1, "Actions: ", temp_actions);
			String values2 = generateParamOutput(c, temp, 2, "Observations: ", temp_observs);
			if ( values1 != null && values2 != null ) {
				m_log_writer.write(values1+values2);
				m_log_writer.flush();
				return true;
			}
		}

		catch (IOException ioe) {
			System.out.println(ioe);
		}

		return false;
	}

	/** This method generates properly-formatted text of probabilities of each agent by calling helper
	 * method probOutputHelper
	 * @param c a set of given components in which the output data is entailed
	 * @param b the starting index of components to be processed 
	 * @param values a matrix that holds all specified actions and observations of all agents
	 * @return the appropriately-formatted text to be written to a corresponding output file
	 */ 
	private String generateProbOutput(Component[] c, int b, Object[][] values)
	{
		for (int i=b; i<(b+values.length); i++) 
			values[i-b] = ((JList)((JScrollPane)c[i]).getViewport().getView()).getSelectedValues();

		if ( ((String)values[0][0]).equals("--") || 
		     (values.length == 4 && ((String)values[1][0]).equals("--")) )
			return null;

		int flag = -1;
		for (int i=values.length-2; i<values.length; i++) {
			if ( ((String)values[i][0]).equals("--") && (flag == -1) )
				flag = i;
			else if ( !(((String)values[i][0]).equals("--")) && (flag > 0) )
				return null;
		}

		String res = "\r\n";
		switch (b) {
			case 1:  res += "T: "; break;
			case 11: res += "O: "; break;
			case 21: res += "R: "; break;
		}
		return probOutputHelper(c, 0, values, res, b, flag);
	}

	/** This recursive helper method generates properly-formatted text of probabilities of each agent
	 * @param c a set of given components in which the output data is entailed
	 * @param i the starting index of components to be processed 
	 * @param temp a matrix that holds all specified actions and observations of all agents
	 * @param res the resulting String of output text
	 * @param offset an integer specifying the appropriate component containing the output data
	 * @param empty a flag indicating whether all fields have been explicitly specified or not
	 * @return the appropriately-formatted text to be written to a corresponding output file
	 */ 
	private String probOutputHelper(Component[] c, int i, Object[][] temp, String res, int offset, int empty)
	{
		if (i == temp.length) {
			if (empty > 0)
				res += "\r\n";
			return (res + ((JTextArea)c[i+offset]).getText() + "\r\n");
		}

		else if (i < temp.length) {
			if (((String)temp[i][0]).equals("ALL")) 
				return (probOutputHelper(c, i+1, temp, res + "* ", offset, empty));
			else if (!(((String)temp[i][0]).equals("--"))) {
				String s = "";
				for (int j=0; j<temp[i].length; j++)
					s += probOutputHelper(c, i+1, temp, res + ((String)temp[i][j]), offset, empty);
				return s;
			}
			else 
				return (probOutputHelper(c, i+1, temp, res + " ", offset, empty));
		}
		return "error in helper method";
	}

	/** This method processes all data entered by users in GUI screen 3 and transfers the data into a
	 * properly-formatted corresponding text file
	 * @param s a flag differentiating between transitions, observations, and rewards
	 */
	private void processScreenThree(String s)
	{
		Component[] c = getCanvas().getScreen(3).getComponents();
		Object[][] temp;
		int i;
		if (s.endsWith("Transitions")) {
			i = 1;
			temp = new Object[3][];
		} else if (s.endsWith("Observations")) {
			i = 11;
			temp = new Object[3][];
		} else {
			i = 21;
			temp = new Object[4][];
		}

		try {
			String probs = generateProbOutput(c, i, temp);
			if ( probs != null ) {
				m_log_writer.write(probs);
				m_log_writer.flush();
			}
		}
		catch (IOException ioe) { 
			System.out.println(ioe); 
		}
	}


	/** Canvas is a GUI for the MdpPuzzleGenerator class */
	class Canvas extends Thread implements ActionListener
	{
		private JFrame m_frame; //main frame window
		private JPanel m_screen1; // initial screen
		private JPanel m_screen2; // actions/observations screen
		private JPanel m_screen3; // probabilities screen
		private JPanel m_screen4; // end screen

		/** This method displays the appropriate screen when buttons are clicked
		 * @param e an ActionEvent invoked by the JVM when a button is clicked
		 */
		public void actionPerformed(ActionEvent e)
		{
			if (e.getActionCommand().equals("Generate Puzzle")) {
				try {
					if (processScreenOne()) {
						m_parser.parse();
						temp_agents = m_parser.getAgents();
						if (temp_agents == 0)
							temp_agents++;
						setupStates(m_parser.state);
//	setupParams(m_parser., String[] observs);
						createScreen2();
						m_frame.getContentPane().remove(m_screen1);
						m_frame.getContentPane().add(m_screen2);
						m_frame.pack();
					}
				}
				catch (IOException ioe) { System.out.println(ioe); }
			}
			else if (e.getActionCommand().equals("Register Parameters")) {
				try {
					if (processScreenTwo()) {
						m_parser.parse();
						setupParamCombos();
						createScreen3();
						m_frame.getContentPane().remove(m_screen2);
						m_frame.getContentPane().add(m_screen3);
						m_frame.pack();
					}
				}
				catch (Exception exc) { System.out.println(exc); }
			}
			else if (e.getActionCommand().startsWith("Update"))
			{
				processScreenThree(e.getActionCommand());
			}
			else if (e.getActionCommand().startsWith("Clear Values"))
			{
				m_frame.getContentPane().remove(m_screen1);
				createScreen1();
				m_frame.getContentPane().add(m_screen1);
			}
			else if (e.getActionCommand().equals("Accept")) {
				try {
					m_parser.parse();
					createEndScreen();
					m_frame.getContentPane().remove(m_screen3);
					m_frame.getContentPane().add(m_screen4);
					m_frame.pack();
				}
				catch (IOException ioe) { System.out.println(ioe); }
			}

			else if (e.getActionCommand().equals("Create New Problem")) {
				createScreen1();
				m_frame.getContentPane().remove(m_screen4);
				m_frame.getContentPane().add(m_screen1);
				m_frame.pack();
			}
			else if (e.getActionCommand().equals("End Program")) {
				try {
					m_frame.getContentPane().remove(m_screen4);
					m_log_writer.close();
					System.out.println("File " + file_name + " generated sucessfully");
					System.exit(0);
				}
				catch (IOException ioe) { System.out.println(ioe); }
			}
		}

		/** The constructor creates a viewable frame and calls for the creation 
		 * of all relating screens
		 */
		private Canvas()
		{
			try {
				JFrame.setDefaultLookAndFeelDecorated(true);
				m_frame = new JFrame("MDP Puzzle Generator");
				m_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				m_frame.setVisible(true);
			}
			catch (HeadlessException he) { }

			createScreen1();
		}

		/** This method returns any one of the program's GUI screens
		 * @param i the number of screen to be returned
		 * @return the requested GUI screen in the form of a JPanel
		 */
		private JPanel getScreen(int i)
		{
			switch (i) {
				case 1: return m_screen1;
				case 2: return m_screen2;
				case 3: return m_screen3;
			}
			return null;
		}

		/** This method creates the first screen for the MdpPuzzleGenerator 
		 * This screen allows the user to specify the discount, values, states, agents,
		 * start distributions, and a title.
		 */
		private void createScreen1()
		{
			m_screen1 = new JPanel(new SpringLayout());
	
			String[] labels = {  "Title: ", "Discount: ", "Values: ", "States: ", "Agents: ", 
						"Start: ", "ALL Actions: ", "ALL Observtns: " };
			int[] t_fields = { 15, 5, 0, 15, 5, 15, 25, 25 };
			String[] val = { " -- ", "cost", "reward" };
	

			for (int i=0; i<labels.length; i++)
			{
				JLabel l = new JLabel(labels[i], JLabel.LEFT);
				m_screen1.add(l);
	
				if (l.getText().equals("Values: ")) {
					JComboBox values = new JComboBox(val); 
					l.setLabelFor(values);
					m_screen1.add(values);
				} else {
					JTextField textField = new JTextField(t_fields[i]);
					l.setLabelFor(textField);
					m_screen1.add(textField);
				}
			}


			JButton button1 = new JButton("Generate Puzzle");
			button1.addActionListener(this);
			button1.setSelected(true);
			m_screen1.add(button1);

			JButton button2 = new JButton("Clear Values");
			button2.addActionListener(this);
			button1.setSelected(false);
			m_screen1.add(button2);

			SpringUtilities.makeCompactGrid(m_screen1,
        	                        labels.length+1, 2, //rows, cols
        	                        25, 10,        //initX, initY
        	                        25, 10);       //xPad, yPad
					

			m_frame.getRootPane().setDefaultButton(button1);
			m_frame.getContentPane().add(m_screen1);
			m_frame.pack();
			m_frame.setVisible(true);
		}

		/** This method creates the second screen for the MdpPuzzleGenerator 
		 * This screen allows the user to specify each agent's actions and observations
		 */
		private void createScreen2()
		{
			m_screen2 = new JPanel();
			m_screen2.setLayout(null);

			Insets insets = m_screen2.getInsets();
			int x_offset = 25;
			int y_offset = 25;
			int temp_x1 = 10;
			int temp_x2 = 10;
			int totX = 10;
			int totY = 10;

			JLabel a_head = new JLabel("Actions");
			Dimension size_a = a_head.getPreferredSize();
			JLabel o_head = new JLabel("Observations");
			Dimension size_o = o_head.getPreferredSize();
			y_offset += size_o.height + 10;
			
			for (int i=0; i<temp_agents; i++)
			{
				x_offset = 25;

				JLabel l = new JLabel("Agent " + i + ":");
				Dimension size = l.getPreferredSize();
				insert(m_screen2, l, x_offset + insets.left, y_offset + insets.top, size.width, size.height);

				temp_x1 = x_offset + size.width + 25;
				size = insertList(temp_actions, m_screen2, temp_x1, y_offset);

				int temp_y = size.height + 10;
				temp_x2 = size.width;
				size = insertList(temp_observs, m_screen2, temp_x2, y_offset);

				y_offset = temp_y;
			}

			totY = y_offset; // + insets.top + 15;
			x_offset = temp_x1;
			y_offset = 25;

			insert(m_screen2, a_head, x_offset + insets.left, y_offset + insets.top, size_a.width, size_a.height);
			insert(m_screen2, o_head, temp_x2 + insets.left, y_offset + insets.top, size_o.width, size_o.height);

			totX = temp_x2 + size_o.width + 25 + insets.left;

			JButton params = new JButton("Register Parameters");
			Dimension size_b = params.getPreferredSize();
			params.addActionListener(this);
			insert(m_screen2, params, totX/2-size_b.width/2 + insets.left, totY + insets.top, size_b.width, size_b.height);

			totY += size_b.height + insets.top + 15;
			totX = Math.max(totX, totX/2+size_b.width+25);
			m_screen2.setPreferredSize(new Dimension(totX, totY));
		}

		/** This method creates the third screen for the MdpPuzzleGenerator 
		 * This screen allows the user to define transitional, observational, and
		 * reward probabilities
		 */
		private void createScreen3()
		{
			m_screen3 = new JPanel();
			m_screen3.setLayout(null);

			Insets insets = m_screen3.getInsets();
			int x_offset = 25;
			int y_offset = 25;
			Dimension tot_size = new Dimension(10, 10);

			String[] prob = { "Set Transitional Probabilities:", 
				"Set Observational Probabilities:", "Set Reward Probabilities:" };

			for (int i=0; i<3; i++)
			{

//		*************************
//		*** Create Components ***
//		*************************
				JLabel a_head = new JLabel("Actions");
				Dimension size_a = a_head.getPreferredSize();
				JLabel s_head = new JLabel("S_State");
				Dimension size_s = s_head.getPreferredSize();
				JLabel e_head = new JLabel("E_State");
				Dimension size_e = e_head.getPreferredSize();
				JLabel o_head = new JLabel("Observe");
				Dimension size_o = o_head.getPreferredSize();
				JLabel p_head = new JLabel("Probabilities");
				Dimension size_p = p_head.getPreferredSize();
				JTextArea text = new JTextArea(m_parser.getStates(),15);
				Dimension size_t = text.getPreferredSize();
				JButton update = new JButton("Update");
				update.addActionListener(this);

//		********************************
//		*** Declare Probability Type ***
//		********************************
				JLabel l = new JLabel(prob[i]);
				Dimension size = l.getPreferredSize();
				insert(m_screen3, l, x_offset-15+ insets.left, y_offset + insets.top, size.width, size.height);

				int x_s=0, x_e=0, x_o=0, x_p=0, y=0;
				y = y_offset + size.height + 10;
				y_offset = y + size_a.height + 10;

//		******************************
//		*** Insert Parameter Lists ***
//		******************************
				size = insertList(m_actions, m_screen3, x_offset, y_offset);
					x_s = size.width;
				size = insertList(temp_states, m_screen3, size.width, y_offset);

				if (i != 1) {
					x_e = size.width;
					size = insertList(temp_states, m_screen3, size.width, y_offset);
				}
				if (i != 0) {
					x_o = size.width;
					size = insertList(m_observs, m_screen3, size.width, y_offset);
				}
					x_p = size.width;

//		**********************************
//		*** Insert TextArea and Button ***
//		**********************************
				insert(m_screen3, text, size.width + insets.left, y_offset + insets.top, size_t.width, size_t.height);
				size.width += size_t.width + 25;
				int temp_y = size_t.height + 50;

				if (i == 0)
					update.setText(update.getText() + " Transitions");
				else if (i == 1)
					update.setText(update.getText() + " Observations");
				else
					update.setText(update.getText() + " Rewards");
				Dimension size_b = update.getPreferredSize();
				insert(m_screen3, update, size.width + insets.left, y_offset + insets.top, size_b.width, size_b.height);
				size.width += size_b.width + 25;


//		*******************************
//		*** Insert Parameter Labels ***
//		*******************************
				if (i == 2)
					tot_size = new Dimension(size.width + insets.left, y_offset + temp_y + insets.top);
				x_offset = 25;

				insert(m_screen3, a_head, x_offset + insets.left, y + insets.top, size_a.width, size_a.height);
				if (i != 1) 
					insert(m_screen3, s_head, x_s + insets.left, y + insets.top, size_s.width, size_s.height);
				else
					insert(m_screen3, e_head, x_s + insets.left, y + insets.top, size_s.width, size_s.height);
				if (x_e > 0)
					insert(m_screen3, e_head, x_e + insets.left, y + insets.top, size_s.width, size_s.height);
				if (x_o > 0) 
					insert(m_screen3, o_head, x_o + insets.left, y + insets.top, size_o.width, size_o.height);
				insert(m_screen3, p_head, x_p + insets.left, y + insets.top, size_p.width, size_p.height);

				y_offset += temp_y;
			}

			JButton finished = new JButton("Accept");
			finished.addActionListener(this);
			Dimension size_f = finished.getPreferredSize();
			insert(m_screen3, finished, tot_size.width/2 - size_f.width/2, tot_size.height, size_f.width, size_f.height);

			tot_size.height += size_f.height + 25;
			m_screen3.setPreferredSize(tot_size);
		}

		/** This method creates the last screen for the MdpPuzzleGenerator 
		 * This screen allows the user to terminate the program or create a new Mdp Puzzle
		 */
		private void createEndScreen()
		{
			m_screen4 = new JPanel();
			m_screen4.setLayout(null);

			Insets insets = m_screen4.getInsets();
			int x_offset = 25;
			int y_offset = 25;
			Dimension tot_size = new Dimension(10, 10);

			JLabel message = new JLabel("Your file has been successfully saved under file name: " + file_name);
			Dimension message_size = message.getPreferredSize();
			insert(m_screen4, message, x_offset, y_offset, message_size.width, message_size.height);

			y_offset += message_size.height + 10;

			JButton finish = new JButton("End Program");
			finish.addActionListener(this);
			Dimension f_size = finish.getPreferredSize();
			insert(m_screen4, finish, x_offset, y_offset, f_size.width, f_size.height);

			JButton create = new JButton("Create New Problem");
			create.addActionListener(this);
			Dimension r_size = create.getPreferredSize();
			insert(m_screen4, create, x_offset*2 + f_size.width, y_offset, r_size.width, r_size.height);

			tot_size.height = y_offset + f_size.height + 25;
			tot_size.width = Math.max(2*x_offset + message_size.width, 3*x_offset + f_size.width + r_size.width);
			m_screen4.setPreferredSize(tot_size);
		}


		/** This is a helper method designed to create and insert JLists into a JPanel
		 * @param s the pre-defined list of values that will be maintained in the JList
		 * @param jp the corresponding JPanel to which the JList will be added
		 * @param x_offset the starting X-coordinate of the JList to be added
		 * @param y_offset the starting Y-coordinate of the JList to be added
		 * @return the coordinates available to the next component
		 */
		private Dimension insertList(String[] s, JPanel jp, int x_offset, int y_offset)
		{
			Insets insets = jp.getInsets();
			JList l = new JList(s);
			l.setSelectedIndex(0);
			l.setVisibleRowCount(3);
			JScrollPane scrollPane = new JScrollPane(l);
			Dimension size = scrollPane.getPreferredSize();
			insert(jp, scrollPane, x_offset + insets.left, y_offset + insets.top, size.width, size.height);
			return (new Dimension(x_offset + size.width + 25, y_offset + size.height + 10));
		}

		/** This is a helper method designed to insert JComponents into a JPanel
		 * @param jc the pre-defined JComponent that will be added to the JPanel
		 * @param jp the corresponding JPanel to which the JComponent will be added
		 * @param x the starting X-coordinate of the JComponent to be added
		 * @param y the starting Y-coordinate of the JComponent to be added
		 * @param w the width of the JComponent to be added
		 * @param h the height of the JComponent to be added
		 */
		private void insert(JPanel jp, JComponent jc, int x, int y, int w, int h)
		{
			jc.setBounds(x, y, w, h);
			jp.add(jc);
		}

		/** This method initializes a new thread whose purpose is to display the Canvas */	
		public void run()
		{
			m_frame.repaint();
		}
	}
}
