import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.*;
import javax.swing.event.*;
import java.text.*;
import java.util.*;
import java.util.Vector;
import java.awt.geom.*;

public class Mesh extends WindowAdapter implements ActionListener 
{
	// fields -------------------------------------------------------------------
	
	// constants and default settings
	
	// default values and min/max values for the domain
	private final static double DOMAIN_X_MIN = -100;
	private final static double DOMAIN_X_MAX = 100;
	private final static double DOMAIN_Y_MIN = -100;
	private final static double DOMAIN_Y_MAX = 100;
	
	private final static double DOMAIN_X_MIN_DEFAULT = -2;
	private final static double DOMAIN_X_MAX_DEFAULT = 2;
	private final static double DOMAIN_Y_MIN_DEFAULT = -2;
	private final static double DOMAIN_Y_MAX_DEFAULT = 2;
	
	// default values and min/max values for the viewable region
	private final static double VIEW_X_MIN = -100;
	private final static double VIEW_X_MAX = 100;
	private final static double VIEW_Y_MIN = -100;
	private final static double VIEW_Y_MAX = 100;
	
	private final static double VIEW_X_MIN_DEFAULT = -5;
	private final static double VIEW_X_MAX_DEFAULT = 5;
	private final static double VIEW_Y_MIN_DEFAULT = -5;
	private final static double VIEW_Y_MAX_DEFAULT = 5;	
	
	// default values for the parameters A,B,C,D
	private final static double A_RE_DEFAULT = 0;
	private final static double B_RE_DEFAULT = 0;
	private final static double C_RE_DEFAULT = 1;
	private final static double D_RE_DEFAULT = 0;
	
	private final static double A_IM_DEFAULT = 0;
	private final static double B_IM_DEFAULT = 0;
	private final static double C_IM_DEFAULT = 0;
	private final static double D_IM_DEFAULT = 0;	
	
	// default values and min/max values for the image size 
	private final static int     WIDTH_DEFAULT = 800;
	private final static int     HEIGHT_DEFAULT = 800;
	private final static int     WIDTH_MIN = 50;
	private final static int     WIDTH_MAX = 2000;
	private final static int     HEIGHT_MIN = 50;
	private final static int     HEIGHT_MAX = 2000;	
	
	// default values and min/max values for the mesh resolution
	private final static int     RES_X_DEFAULT = 50;
	private final static int     RES_Y_DEFAULT = 50;
	private final static int	 RES_MIN = 1;
	private final static int	 RES_MAX = 500;
		
	// min and max values for spinners
	private static double domainXMin = DOMAIN_X_MIN_DEFAULT;  // minimum x-value of domain
	private static double domainXMax = DOMAIN_X_MAX_DEFAULT;  // maximum x-value of domain
	private static double domainYMin = DOMAIN_Y_MIN_DEFAULT;  // minimum y-value of domain
	private static double domainYMax = DOMAIN_Y_MAX_DEFAULT;  // maximum y-value of domain
	
	private static double viewXMin = VIEW_X_MIN_DEFAULT;  // minimum x-value of viewed range
	private static double viewXMax = VIEW_X_MAX_DEFAULT;  // maximum x-value of viewed range
	private static double viewYMin = VIEW_Y_MIN_DEFAULT;  // minimum y-value of viewed range
	private static double viewYMax = VIEW_Y_MAX_DEFAULT;  // maximum y-value of viewed range
	
	// defaults for radio buttons
	private final static int MAP_DEFAULT = 0;   // defaults to a polynomial
	private final static int AXES_DEFAULT = 0;  // defaults to "on"
	
	private final static String MESSAGE_DEFAULT = "Press \"Draw\" to begin..."; // default display message
		
	// variables		

	// data structures for the mesh
	private static ComplexNumber nodes[][];
	private static Vector<ComplexNumber[]> segments = new Vector<ComplexNumber[]>();
	private static ComplexNumber seg[];
	
	//  the parameters A,B,C,D
	private static ComplexNumber varA = new ComplexNumber(A_RE_DEFAULT,A_IM_DEFAULT);
	private static ComplexNumber varB = new ComplexNumber(B_RE_DEFAULT,B_IM_DEFAULT);
	private static ComplexNumber varC = new ComplexNumber(C_RE_DEFAULT,C_IM_DEFAULT);
	private static ComplexNumber varD = new ComplexNumber(D_RE_DEFAULT,D_IM_DEFAULT);
	
	// height, radio buttons, message resolution,
	private static int     width  = WIDTH_DEFAULT;		
	private static int     height = HEIGHT_DEFAULT;   	
	private static int     map = MAP_DEFAULT;
	private static int     axes = AXES_DEFAULT;
	private static String  message = MESSAGE_DEFAULT;
	private static int     resX = RES_X_DEFAULT;
	private static int     resY = RES_Y_DEFAULT;
		
	// values taken in by the gui components
	private static int     spinnerWidth = width;			
	private static int     spinnerHeight = height;		
	private static double  spinnerDomainXMin = domainXMin; 
	private static double  spinnerDomainXMax = domainXMax;
	private static double  spinnerDomainYMin = domainYMin; 
	private static double  spinnerDomainYMax = domainYMax;
	private static double  spinnerViewXMin = viewXMin; 
	private static double  spinnerViewXMax = viewXMax;
	private static double  spinnerViewYMin = viewYMin; 
	private static double  spinnerViewYMax = viewYMax;
	private static int     spinnerResX = resX;
	private static int     spinnerResY = resY;
	
	// how much the spinners change
	private final static double SPINNER_STEP = 0.1;
	private final static int WINDOW_SPINNER_STEP = 10;
	private final static int RES_SPINNER_STEP = 1;
	
	// technical stuff 
	private final static int     xWindow = 10;  				// pixels added to width by window border
	private final static int     yWindow = 33; 				// pixels added to height by window title bar and bottom border
	private final static Insets  inset = new Insets(0,0,0,0);  	// used to make margins 0	
	private final static String  CONTROL_TITLE = "Control Window";	
	private final static String  IMAGE_TITLE = "Display Window";
	
	private static BufferedImage bi; 			// this is the actual image
	private static Graphics2D bg; 			// needed to draw the pixel in bi
	private static ImageIcon imageIcon; 		// used to put bi into a controlFrame
	
	// GUI components	
	
	// basic windows
	private static JFrame controlFrame; // control window 
   	private static JFrame imageFrame;   // display window
	private static JLabel imageLabel;   // the label holds the image, which is imageIcon above
	
	// mapping type controls
	private static JLabel mapModeLabel = new JLabel("Mapping type:");
	private static JRadioButton[] mapModeRadio = { new JRadioButton("Polynomial: Az^3 + Bz^2 + Cz + D"), 
												   new JRadioButton("Mobius Transform: (Az + B)/(Cz + D)"),
												   new JRadioButton("Exponential: A*exp(Bz + C) + D") };	
	
	// parameter input
	private static JLabel varALabel = new JLabel("Parameter A");
	private static JLabel varBLabel = new JLabel("Parameter B");
	private static JLabel varCLabel = new JLabel("Parameter C");
	private static JLabel varDLabel = new JLabel("Parameter D");	
	
	private static JLabel plusALabel = new JLabel("+");
	private static JLabel iALabel = new JLabel("i");
	private static JLabel plusBLabel = new JLabel("+");
	private static JLabel iBLabel = new JLabel("i");
	private static JLabel plusCLabel = new JLabel("+");
	private static JLabel iCLabel = new JLabel("i");
	private static JLabel plusDLabel = new JLabel("+");
	private static JLabel iDLabel = new JLabel("i");
		
	private static JTextField varAreField = new JTextField(Double.toString(A_RE_DEFAULT),10);
	private static JTextField varBreField = new JTextField(Double.toString(B_RE_DEFAULT),10);
	private static JTextField varCreField = new JTextField(Double.toString(C_RE_DEFAULT),10);
	private static JTextField varDreField = new JTextField(Double.toString(D_RE_DEFAULT),10);	
	
	private static JTextField varAimField = new JTextField(Double.toString(A_IM_DEFAULT),10);
	private static JTextField varBimField = new JTextField(Double.toString(B_IM_DEFAULT),10);
	private static JTextField varCimField = new JTextField(Double.toString(C_IM_DEFAULT),10);
	private static JTextField varDimField = new JTextField(Double.toString(D_IM_DEFAULT),10);	
	
	// resolution control
	private static JLabel resLabel = new JLabel("Mesh Resolution  ");	
	private static JLabel resXLabel = new JLabel("X:");
	private static SpinnerNumberModel resXModel = new SpinnerNumberModel(resX,RES_MIN,RES_MAX,RES_SPINNER_STEP);      
	private static JSpinner resXSpinner = new JSpinner(resXModel);
	private static JLabel resYLabel = new JLabel("Y:");
	private static SpinnerNumberModel resYModel = new SpinnerNumberModel(resY,RES_MIN,RES_MAX,RES_SPINNER_STEP);      
	private static JSpinner resYSpinner = new JSpinner(resYModel);
	
	// domain control
	private static JLabel domainLabel = new JLabel("Domain  ");
	private static JLabel domainXMinLabel = new JLabel("X min:");
	private static JLabel domainXMaxLabel = new JLabel("X max:");
	private static SpinnerNumberModel domainXMinModel = new SpinnerNumberModel(domainXMin,DOMAIN_X_MIN,DOMAIN_X_MAX,SPINNER_STEP);
	private static JSpinner domainXMinSpinner = new JSpinner(domainXMinModel);        
	private static SpinnerNumberModel domainXMaxModel = new SpinnerNumberModel(domainXMax,DOMAIN_X_MIN,DOMAIN_X_MAX,SPINNER_STEP);
	private static JSpinner domainXMaxSpinner = new JSpinner(domainXMaxModel);   
	
	private static JLabel domainYMinLabel = new JLabel("Y min:");
	private static JLabel domainYMaxLabel = new JLabel("Y max:");
	private static SpinnerNumberModel domainYMinModel = new SpinnerNumberModel(domainYMin,DOMAIN_Y_MIN,DOMAIN_Y_MAX,SPINNER_STEP);
	private static JSpinner domainYMinSpinner = new JSpinner(domainYMinModel);        
	private static SpinnerNumberModel domainYMaxModel = new SpinnerNumberModel(domainYMax,DOMAIN_Y_MIN,DOMAIN_Y_MAX,SPINNER_STEP);
	private static JSpinner domainYMaxSpinner = new JSpinner(domainYMaxModel);        
   	
	// viewable region control
	private static JLabel viewLabel = new JLabel("Visible Region  ");
	private static JLabel viewXMinLabel = new JLabel("X min:");
	private static JLabel viewXMaxLabel = new JLabel("X max:");
	private static SpinnerNumberModel viewXMinModel = new SpinnerNumberModel(viewXMin,VIEW_X_MIN,VIEW_X_MAX,SPINNER_STEP);
	private static JSpinner viewXMinSpinner = new JSpinner(viewXMinModel);        
	private static SpinnerNumberModel viewXMaxModel = new SpinnerNumberModel(viewXMax,VIEW_X_MIN,VIEW_X_MAX,SPINNER_STEP);
	private static JSpinner viewXMaxSpinner = new JSpinner(viewXMaxModel);        
	
	private static JLabel viewYMinLabel = new JLabel("Y min:");
	private static JLabel viewYMaxLabel = new JLabel("Y Max:");
	private static SpinnerNumberModel viewYMinModel = new SpinnerNumberModel(viewYMin,VIEW_Y_MIN,VIEW_Y_MAX,SPINNER_STEP);
	private static JSpinner viewYMinSpinner = new JSpinner(viewYMinModel);        	
	private static SpinnerNumberModel viewYMaxModel = new SpinnerNumberModel(viewYMax,VIEW_Y_MIN,VIEW_Y_MAX,SPINNER_STEP);
	private static JSpinner viewYMaxSpinner = new JSpinner(viewYMaxModel);        	
	
	// coordinate axes control
	private static JLabel axesLabel = new JLabel("Coordinate Axes:");	
	private static JRadioButton[] axesRadio = { new JRadioButton("On"), 
												new JRadioButton("Off")};
	
	// image size control
	private static JLabel xLabel = new JLabel(" Image Width: ");
	private static SpinnerNumberModel xModel = new SpinnerNumberModel(width,WIDTH_MIN,WIDTH_MAX,WINDOW_SPINNER_STEP);                			
	private static JSpinner xSpinner = new JSpinner(xModel);        
	private static JLabel yLabel = new JLabel(" Image Height: ");        
	private static SpinnerNumberModel yModel = new SpinnerNumberModel(height,WIDTH_MIN,WIDTH_MAX,WINDOW_SPINNER_STEP);      
	private static JSpinner ySpinner = new JSpinner(yModel);
	
	// command buttons
	private static JButton drawMeshButton = new JButton("Draw");
	private static JButton saveButton = new JButton("Save Image");	
	private static JButton resetButton = new JButton("Reset");	
												   
   	// constants for action commands	
	protected final static String DRAW = "draw";
	protected final static String SAVE = "save";	
	protected final static String RESET = "reset";	
   	
	// message box											   
	private static JTextField messageBox = new JTextField(MESSAGE_DEFAULT);	

   
	// end of fields -----------------------------------------------------------------------   
   
   
	public Mesh() { }
	
	
	// this handles the domain spinners
	ChangeListener domainListener = new ChangeListener() 
	{
		public void stateChanged(ChangeEvent e) 
		{			
			// get the data from the spinners
			spinnerDomainXMin = domainXMinModel.getNumber().doubleValue();
			spinnerDomainXMax = domainXMaxModel.getNumber().doubleValue();
			spinnerDomainYMin = domainYMinModel.getNumber().doubleValue();
			spinnerDomainYMax = domainYMaxModel.getNumber().doubleValue();
			
			// need to make sure that xMin < xMax and yMin < yMax
			if( (spinnerDomainXMin < spinnerDomainXMax) && (spinnerDomainYMin < spinnerDomainYMax) )
			{
				message = "Domain size changed to [" + Double.toString(spinnerDomainXMin) + ", " + Double.toString(spinnerDomainXMax) + "] x [" + Double.toString(spinnerDomainYMin) + ", " + Double.toString(spinnerDomainYMax) + "]";						
				//System.out.println(message);			
				messageBox.setText(message);
			}
			else{
				// switch the incriminating values
				if( spinnerDomainXMin >= spinnerDomainXMax)
				{
					domainXMinModel.setValue(spinnerDomainXMax);
					domainXMaxModel.setValue(spinnerDomainXMin);
					
					message = "Incorrect domain values!  Check x-values.";
					System.out.println(message);
					messageBox.setText(message);
				}
				else
				{
					domainYMinModel.setValue(spinnerDomainYMax);
					domainYMaxModel.setValue(spinnerDomainYMin);
					
					message = "Incorrect domain values!  Check y-values.";
					System.out.println(message);
					messageBox.setText(message);
				}				
			}
			
		}		
	};	
	
	// this handles the view spinners
	ChangeListener viewListener = new ChangeListener() 
	{
		public void stateChanged(ChangeEvent e) 
		{			
			// get the data from the spinners
			spinnerViewXMin = viewXMinModel.getNumber().doubleValue();
			spinnerViewXMax = viewXMaxModel.getNumber().doubleValue();
			spinnerViewYMin = viewYMinModel.getNumber().doubleValue();
			spinnerViewYMax = viewYMaxModel.getNumber().doubleValue();
			
			// need to make sure that xMin < xMax and yMin < yMax
			if( (spinnerViewXMin < spinnerViewXMax) && (spinnerViewYMin < spinnerViewYMax) )
			{
				message = "View size changed to [" + Double.toString(spinnerViewXMin) + ", " + Double.toString(spinnerViewXMax) + "] x [" + Double.toString(spinnerViewYMin) + ", " + Double.toString(spinnerViewYMax) + "]";						
				//System.out.println(message);
				messageBox.setText(message);
			}
			else{
				// switch the incriminating values
				if( spinnerViewXMin >= spinnerViewXMax)
				{
					viewXMinModel.setValue(spinnerViewXMax);
					viewXMaxModel.setValue(spinnerViewXMin);
					
					message = "Incorrect view values!  Check x-values.";
					System.out.println(message);
					System.out.println("current values are: " + Double.toString(spinnerViewXMin) + ", " + Double.toString(spinnerViewXMax));
					messageBox.setText(message);
				}
				else
				{
					viewYMinModel.setValue(spinnerViewYMax);
					viewYMaxModel.setValue(spinnerViewYMin);
					
					message = "Incorrect view values!  Check y-values.";
					System.out.println(message);
					System.out.println("current values are: " + Double.toString(spinnerViewYMin) + ", " + Double.toString(spinnerViewYMax));
					messageBox.setText(message);
				}				
			}
			
		}		
	};		
	
	// this handles the res spinners
	ChangeListener resListener = new ChangeListener() 
	{
		public void stateChanged(ChangeEvent e) 
		{			
			spinnerResX = resXModel.getNumber().intValue();
			spinnerResY = resYModel.getNumber().intValue();
			message = "Resolution changed to X=" + Integer.toString(spinnerResX) + ", Y=" + Integer.toString(spinnerResY);
			//System.out.println(message);
			messageBox.setText(message);
		}		
	};	
	
	
	// this handles the size spinners
	ChangeListener sizeListener = new ChangeListener() 
	{
		public void stateChanged(ChangeEvent e) 
		{			
			spinnerWidth = xModel.getNumber().intValue();
			spinnerHeight = yModel.getNumber().intValue();
			message = "Image size changed to X=" + Integer.toString(spinnerWidth) + ", Y=" + Integer.toString(spinnerHeight);
			//System.out.println(message);
			messageBox.setText(message);
		}		
	};

	
	//show the image window
	public static void showNewWindow() 
	{ 
		imageFrame.setSize(width+xWindow,height+yWindow);              
		imageFrame.setVisible(true);
	}

	// Create the window-creation controls that go in the main window.
	protected JComponent createOptionControls() 
	{    	
		// panel to hold map label and radio buttons
		JPanel mapPanel = new JPanel();
						
		JPanel mapRadioPanel = new JPanel();
			mapRadioPanel.setLayout(new BoxLayout(mapRadioPanel,BoxLayout.Y_AXIS));	
			mapRadioPanel.add(mapModeRadio[0]);
			mapRadioPanel.add(mapModeRadio[1]);
			mapRadioPanel.add(mapModeRadio[2]);
		
		ButtonGroup mapGroup = new ButtonGroup();
			mapGroup.add(mapModeRadio[0]);
			mapGroup.add(mapModeRadio[1]);
			mapGroup.add(mapModeRadio[2]);
		    mapModeRadio[0].setSelected(true);
		
			mapPanel.add(mapModeLabel);					
			mapPanel.add(mapRadioPanel);				
			
		// panel to hold stuff for parameters A,B,C,D
		JPanel varPanel = new JPanel();
			varPanel.setLayout(new BoxLayout(varPanel,BoxLayout.Y_AXIS));	
		JPanel varAPanel = new JPanel();
			varAPanel.add(varALabel);
			varAPanel.add(varAreField);
			varAPanel.add(plusALabel);
			varAPanel.add(varAimField);
			varAPanel.add(iALabel);
		JPanel varBPanel = new JPanel();		
			varBPanel.add(varBLabel);
			varBPanel.add(varBreField);
			varBPanel.add(plusBLabel);
			varBPanel.add(varBimField);
			varBPanel.add(iBLabel);
		JPanel varCPanel = new JPanel();
			varCPanel.add(varCLabel);
			varCPanel.add(varCreField);
			varCPanel.add(plusCLabel);
			varCPanel.add(varCimField);
			varCPanel.add(iCLabel);
		JPanel varDPanel = new JPanel();
			varDPanel.add(varDLabel);
			varDPanel.add(varDreField);
			varDPanel.add(plusDLabel);
			varDPanel.add(varDimField);
			varDPanel.add(iDLabel);
			
			varPanel.add(varAPanel);
			varPanel.add(varBPanel);
			varPanel.add(varCPanel);
			varPanel.add(varDPanel);
			
		// panel for resolution controls	
		JPanel resPanel = new JPanel();
			resPanel.add(resLabel);
			resPanel.add(resXLabel);
			resPanel.add(resXSpinner);						
			resPanel.add(resYLabel);
			resPanel.add(resYSpinner);						
			
		// panel for domain controls	
		JPanel domainPanel = new JPanel();
			domainPanel.add(domainLabel);
			domainPanel.add(domainXMinLabel);
			domainPanel.add(domainXMinSpinner);			
			domainPanel.add(domainXMaxLabel);
			domainPanel.add(domainXMaxSpinner);
			domainPanel.add(domainYMinLabel);
			domainPanel.add(domainYMinSpinner);			
			domainPanel.add(domainYMaxLabel);
			domainPanel.add(domainYMaxSpinner);
			
		// panel for viewable region controls	
		JPanel viewPanel = new JPanel();
			viewPanel.add(viewLabel);
			viewPanel.add(viewXMinLabel);
			viewPanel.add(viewXMinSpinner);
			viewPanel.add(viewXMaxLabel);			
			viewPanel.add(viewXMaxSpinner);
			viewPanel.add(viewYMinLabel);
			viewPanel.add(viewYMinSpinner);			
			viewPanel.add(viewYMaxLabel);
			viewPanel.add(viewYMaxSpinner);	
			
		// panel to hold axex label and radio buttons
		JPanel axesPanel = new JPanel();
		
		JPanel axesRadioPanel = new JPanel();
			axesRadioPanel.setLayout(new BoxLayout(axesRadioPanel,BoxLayout.Y_AXIS));	
			axesRadioPanel.add(axesRadio[0]);
			axesRadioPanel.add(axesRadio[1]);
			
		ButtonGroup axesGroup = new ButtonGroup();
			axesGroup.add(axesRadio[0]);
			axesGroup.add(axesRadio[1]);			
		    axesRadio[0].setSelected(true);	

			axesPanel.add(axesLabel);					
			axesPanel.add(axesRadioPanel);					
			
		// panel for image size controls
		JPanel sizePanel = new JPanel();	
			sizePanel.add(xLabel);
			sizePanel.add(xSpinner);
			sizePanel.add(yLabel);
			sizePanel.add(ySpinner);		
		sizePanel.setLayout(new BoxLayout(sizePanel,BoxLayout.X_AXIS));			
	
		// panel for the buttons
		JPanel buttonPanel = new JPanel();
			buttonPanel.add(drawMeshButton);
			drawMeshButton.setActionCommand(DRAW);
			buttonPanel.add(saveButton);
			saveButton.setActionCommand(SAVE);
			buttonPanel.add(resetButton);
			resetButton.setActionCommand(RESET);
		
  		// set listeners
  		drawMeshButton.addActionListener(this);
		saveButton.addActionListener(this);
		resetButton.addActionListener(this);
		domainXMinSpinner.addChangeListener(domainListener);
		domainXMaxSpinner.addChangeListener(domainListener);
		domainYMinSpinner.addChangeListener(domainListener);
		domainYMaxSpinner.addChangeListener(domainListener);
		viewXMinSpinner.addChangeListener(viewListener);
		viewXMaxSpinner.addChangeListener(viewListener);
		viewYMinSpinner.addChangeListener(viewListener);
		viewYMaxSpinner.addChangeListener(viewListener);
   		xSpinner.addChangeListener(sizeListener);
  		ySpinner.addChangeListener(sizeListener);
		resXSpinner.addChangeListener(resListener);
		resYSpinner.addChangeListener(resListener);
		
		//Add everything to a container.
		Box box = Box.createVerticalBox();
			box.add(mapPanel);
			box.add(varPanel);
			box.add(resPanel);
			box.add(domainPanel);
			box.add(viewPanel);
			box.add(axesPanel);
			box.add(sizePanel);
			box.add(buttonPanel);
			box.add(messageBox);
			
		return box;
	}	

	//Handle action events from all the buttons.
	public void actionPerformed(ActionEvent e) 
	{ 	
		String command = e.getActionCommand();
		if( DRAW.equals(command) ) 
		{ 
			message = "the \"Draw\" button was pressed";
			//System.out.println(message); 	
			messageBox.setText(message);
						
			// probably need to check that all this input is valid at some pointl...
			// set the domain information
			domainXMin = spinnerDomainXMin;
			domainXMax = spinnerDomainXMax;
			domainYMin = spinnerDomainYMin;
			domainYMax = spinnerDomainYMax;	
			
			// set viewable region information
			viewXMin = spinnerViewXMin;
			viewXMax = spinnerViewXMax;
			viewYMin = spinnerViewYMin;
			viewYMax = spinnerViewYMax;
			
			// set the resolution imformation
			resX = spinnerResX;
			resY = spinnerResY;

			// set the image size information
			width = spinnerWidth;
			height = spinnerHeight;
						
			// get data from A,B,C,D boxes
			varA = new ComplexNumber( Double.parseDouble(varAreField.getText()), Double.parseDouble(varAimField.getText()) );
			varB = new ComplexNumber( Double.parseDouble(varBreField.getText()), Double.parseDouble(varBimField.getText()) );
			varC = new ComplexNumber( Double.parseDouble(varCreField.getText()), Double.parseDouble(varCimField.getText()) );
			varD = new ComplexNumber( Double.parseDouble(varDreField.getText()), Double.parseDouble(varDimField.getText()) );
			
			//System.out.println("Inputted parameters:");
			//System.out.println("A =" + varA.toString() + ", B =" + varB.toString() + ", C =" + varC.toString() + ", D =" + varD.toString());
			
			// make a new bufferedImage and stuff
			bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
   			bg = bi.createGraphics();
			
			// if the window was closed, open a new one -- move this down??
			if( !imageFrame.isVisible() ) { showNewWindow(); }
			else 
			{
				// need to change the size of this window...	
				imageFrame.setSize(width+xWindow,height+yWindow);
          		imageFrame.requestFocus();
          	}  
			
			// make the double array of nodes, and apply transformation
			transformNodes();
			
			// set the segments		
			segments = new Vector<ComplexNumber[]>();
			setSegments();
						
			//draw the picture
			drawMesh();	
          	imageFrame.requestFocus();			
		}
		else if( SAVE.equals(command) ) 
		{ 		
			message = "the \"Save Image\" button was pressed";
			//System.out.println(message); 	
			messageBox.setText(message);		
			
			// Write image to a file
    		try 
    		{		
     	   		File file = new File(fileNameGenerator());
        		ImageIO.write(bi, "png", file);    
    		}
    		catch (IOException ex) 
			{ 
				message = "Error saving image!";
				System.out.println(message); 
				messageBox.setText(message);
			}				
		}
		else if( RESET.equals(command) )
		{
			message = "the \"Reset\" button was pressed";
			//System.out.println(message); 	
			messageBox.setText(message);
		
			// reset everything
			width  = WIDTH_DEFAULT;		
			height = HEIGHT_DEFAULT;   	
			map = MAP_DEFAULT;
			axes = AXES_DEFAULT;
			message = MESSAGE_DEFAULT;
			resX = RES_X_DEFAULT;
			resY = RES_Y_DEFAULT;
			
			varA = new ComplexNumber(A_RE_DEFAULT,A_IM_DEFAULT);
			varB = new ComplexNumber(B_RE_DEFAULT,B_IM_DEFAULT);
			varC = new ComplexNumber(C_RE_DEFAULT,C_IM_DEFAULT);
			varD = new ComplexNumber(D_RE_DEFAULT,D_IM_DEFAULT);
			
			domainXMin = DOMAIN_X_MIN_DEFAULT;
			domainXMax = DOMAIN_X_MAX_DEFAULT;
			domainYMin = DOMAIN_Y_MIN_DEFAULT;
			domainYMax = DOMAIN_Y_MAX_DEFAULT;
			
			viewXMin = VIEW_X_MIN_DEFAULT;
			viewXMax = VIEW_X_MAX_DEFAULT;
			viewYMin = VIEW_Y_MIN_DEFAULT;
			viewYMax = VIEW_Y_MAX_DEFAULT;	
			
			xSpinner.setValue(WIDTH_DEFAULT);
			ySpinner.setValue(HEIGHT_DEFAULT);
			
			domainXMinSpinner.setValue(DOMAIN_X_MIN_DEFAULT);
			domainXMaxSpinner.setValue(DOMAIN_X_MAX_DEFAULT);
			domainYMinSpinner.setValue(DOMAIN_Y_MIN_DEFAULT);
			domainYMaxSpinner.setValue(DOMAIN_Y_MAX_DEFAULT);
		
			resXSpinner.setValue(RES_X_DEFAULT);
			resYSpinner.setValue(RES_Y_DEFAULT);
	
			viewXMinSpinner.setValue(VIEW_X_MIN_DEFAULT);
			viewXMaxSpinner.setValue(VIEW_X_MAX_DEFAULT);
			viewYMinSpinner.setValue(VIEW_Y_MIN_DEFAULT);
			viewYMaxSpinner.setValue(VIEW_Y_MAX_DEFAULT);	
	
			varAreField.setText(Double.toString(A_RE_DEFAULT));
			varBreField.setText(Double.toString(B_RE_DEFAULT));
			varCreField.setText(Double.toString(C_RE_DEFAULT));
			varDreField.setText(Double.toString(D_RE_DEFAULT));
	
			varAimField.setText(Double.toString(A_IM_DEFAULT));
			varBimField.setText(Double.toString(B_IM_DEFAULT));
			varCimField.setText(Double.toString(C_IM_DEFAULT));
			varDimField.setText(Double.toString(D_IM_DEFAULT));					
			
			axesRadio[AXES_DEFAULT].setSelected(true);
			mapModeRadio[MAP_DEFAULT].setSelected(true);
			
			// make a new bufferedImage and stuff
			bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
   			bg = bi.createGraphics();
			
			// make the double array of nodes, and apply transformation
			transformNodes();
			
			// set the segments		
			segments = new Vector<ComplexNumber[]>();
			setSegments();
			
			drawMesh();
			
			// if the window was closed, open a new one of proper size
			if( !imageFrame.isVisible() ) 
			{ 
				showNewWindow(); 
			}
			else // update the size of the open window
			{
				imageFrame.setSize(width+xWindow,width+yWindow);
          		imageFrame.requestFocus();
			}
			
		}
		else{
			message = "oops!";
			System.out.println(message); 
			messageBox.setText(message);
		}                
    }
  
	// applies the transformation to each node  //can definite make this more efficient putting first loop inside the others
	public static void transformNodes()
	{	
		// make the double array of nodes, changing space coordinates to actual window coordinates
		nodes = new ComplexNumber[resX+1][resY+1];
			
		double delX = (domainXMax - domainXMin)/resX;
		double delY = (domainYMax - domainYMin)/resY;
			
		for(int i=0; i < resX+1; i++)
		{
			for(int j=0; j < resY+1; j++)
			{				
				nodes[i][j] = new ComplexNumber(domainXMin + delX*i,domainYMin + delY*j);
			}
		}
	
		// check which map to apply, then apply it!
		if(mapModeRadio[0].isSelected())
		{
			//System.out.println("applying polynomial with A=" + varA.toString() + ", B=" + varB.toString() + ", C=" + varC.toString() + ", D=" + varD.toString());
			map = 0;
			for(int i = 0; i < resX+1; i++) 
			{
				for(int j = 0; j < resY+1; j++) 
				{
					nodes[i][j] = cubic(nodes[i][j]);		
				}	
			}	
		}
		else if(mapModeRadio[1].isSelected())
		{
			//System.out.println("applying mobius with A=" + varA.toString() + ", B=" + varB.toString() + ", C=" + varC.toString() + ", D=" + varD.toString());
			map = 1;
			for(int i = 0; i < resX+1; i++) 
			{
				for(int j = 0; j < resY+1; j++) 
				{
					nodes[i][j] = mobius(nodes[i][j]);				
				}	
			}
		}
		else
		{
			//System.out.println("applying exponential with A=" + varA.toString() + ", B=" + varB.toString() + ", C=" + varC.toString() + ", D=" + varD.toString());
			map = 2;
			for(int i = 0; i < resX+1; i++) 
			{
				for(int j = 0; j < resY+1; j++) 
				{
					nodes[i][j] = exponential(nodes[i][j]);				
				}	
			}
		}
		
	}

	public static ComplexNumber cubic(ComplexNumber z)
	{
		//System.out.print("transforming " + z.toString() + " |-------> ");
		ComplexNumber w = varA.mult(z.cube()).add(varB.mult(z.square()).add(varC.mult(z)).add(varD));
		//System.out.println(w.toString());	
		return w;
	}
	
	public static ComplexNumber mobius(ComplexNumber z)
	{
		//System.out.print("transforming " + z.toString() + " |-------> ");
		ComplexNumber w = ( varA.mult(z).add(varB) ).div( varC.mult(z).add(varD) );
		//System.out.println(w.toString());	
		return w;
	}
	
	public static ComplexNumber exponential(ComplexNumber z)
	{
		//System.out.print("transforming " + z.toString() + " |-------> ");
		//System.out.println(z.real());
		double x = (varC.add(varB.mult(z))).real();
		double y = (varC.add(varB.mult(z))).imag();
		ComplexNumber w = new ComplexNumber(Math.exp(x)*Math.cos(y),Math.exp(x)*Math.sin(y));
		ComplexNumber v = varD.add(varA.mult(w));
		//System.out.println(v.toString());	
		return v;
	}	
	
	// fill a Vector with line segments joining the appropriate nodes
	public static void setSegments()
	{			
		int i,j;
		for(j = 0; j < resY+1; j++) 
		{
			for(i = 0; i < resX; i++) 
			{
				if((i+1)%(resX+1) !=0) 
				{ // join nodes left to right
					seg = new ComplexNumber[2];
					seg[0] = nodes[i][j];
					seg[1] = nodes[i+1][j];
					segments.add(seg);
					//System.out.println("added a segment from " + seg[0].toString() + " to " + seg[1].toString());
				}
			}
		}
        
		for(i = 0; i < resX+1; i++) 
		{
			for(j = 0; j < resY; j++) 
			{
				if((j+1)%(resY+1) !=0) 
				{ // join nodes top to bottom
					seg = new ComplexNumber[2];
					seg[0] = nodes[i][j];
					seg[1] = nodes[i][j+1];
					segments.add(seg);
					//System.out.println("added a segment from " + seg[0].toString() + " to " + seg[1].toString());
				}
			}
		}
	}
	
	
    //Creates the Image 
	public static void drawMesh() 
	{    	
		// initialize the background to white
		bg.setColor(Color.white);
		bg.fillRect(0,0,width,height);	
    					
		//System.out.println("there are " + segments.size() + " segments in the vector");
		
		// draw the coordinate axes:
		if( axesRadio[0].isSelected() )
		{
			bg.setColor(Color.blue);
			bg.drawLine(Math.round(Math.round(spaceToScreenX(viewXMin))),
						Math.round(Math.round(spaceToScreenY(0))),
						Math.round(Math.round(spaceToScreenX(viewXMax))),
						Math.round(Math.round(spaceToScreenY(0))));
			bg.drawLine(Math.round(Math.round(spaceToScreenX(0))),
						Math.round(Math.round(spaceToScreenY(viewYMin))),
						Math.round(Math.round(spaceToScreenX(0))),
						Math.round(Math.round(spaceToScreenY(viewYMax))));					
		}
		
		//System.out.println("vertices of y-axis: (" + Math.round(Math.round(spaceToScreenX(0))) + ", " + Math.round(Math.round(spaceToScreenX(viewYMin))) + ")  (" + Math.round(Math.round(spaceToScreenX(0))) + ", " + Math.round(Math.round(spaceToScreenX(viewYMax))) + ")");
		
		
		// draw all of the segments in the vector
		bg.setColor(Color.black);
        for(int i = 0; i < segments.size(); i++) 
		{					
			//System.out.println("parsing segments list: segments.get(" + i + ") = "  + segments.get(i)[0].toString() + " ... " + segments.get(i)[1].toString() );
			
			// get segments, and transform the space coordinates to window coordinates
			bg.drawLine(Math.round(Math.round(spaceToScreenX(segments.get(i)[0].real()))),   //x_1
						Math.round(Math.round(spaceToScreenY(segments.get(i)[0].imag()))),   //y_1 
						Math.round(Math.round(spaceToScreenX(segments.get(i)[1].real()))),   //x_2
						Math.round(Math.round(spaceToScreenY(segments.get(i)[1].imag()))));  //y_2
						
			//System.out.println("Drawing a segment from " + "(" + Math.round(Math.round(spaceToScreenX(segments.get(i)[0].real()))) + ", " + Math.round(Math.round(spaceToScreenY(segments.get(i)[0].imag()))) + ") to (" + Math.round(Math.round(spaceToScreenX(segments.get(i)[0].real()))) + ", " + Math.round(Math.round(spaceToScreenY(segments.get(i)[1].imag()))) + ")");				
						
		}
		
		// set the image icon
      	imageIcon = new ImageIcon(bi);
      	imageLabel.setIcon(imageIcon);
		
		// sanity check
		//System.out.println("width= " + width + ", height= " + height);
		//System.out.println("domainXMin= " + domainXMin + ", domainXMax= " + domainXMax);
		//System.out.println("domainYMin= " + domainYMin + ", domainYMax= " + domainYMax);
		//System.out.println("viewXMin= " + viewXMin + ", viewXMax= " + viewXMax);
		//System.out.println("viewYMin= " + viewYMin + ", viewYMax= " + viewYMax);			
    }
    
	// converts space coordinates to window coordinates, for the x coordinate
	public static double spaceToScreenX(double x)
	{
		return width*(x - viewXMin)/(viewXMax - viewXMin);
	}
	
	// converts space coordinates to window coordinates, for the y coordinate
	public static double spaceToScreenY(double y)
	{
		return height*(viewYMax - y)/(viewYMax - viewYMin);
	}
	
	// returns a file name when an image is to be saved
	public String fileNameGenerator()
	{		
		String mapType = "";
		if(map == 0){ mapType = "poly"; }
		else if(map == 1){ mapType = "mobius"; }
		else { mapType = "exp"; }
		
		message = "./saved/mesh-" + mapType + "-" + width + "x" + height + "-A" + varA.toString() + "-B" + varB.toString() + "-C" + varC.toString() + "-D" + varD.toString() + "-[" + domainXMin + "," + domainXMax + "]x[" + domainXMin + "," + domainXMax + "].png";
		messageBox.setText("Wrote file: " + message);
		
		return message;
		
	}        		
	
	
	// Create the GUI and show it      
	private static void createAndShowGUI() 
	{  		
		//Use the Java look and feel.
		try
		{
			UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
		} 
		catch (Exception e) { }

		//Make sure we have nice window decorations.
		JFrame.setDefaultLookAndFeelDecorated(true);
		JDialog.setDefaultLookAndFeelDecorated(true);

		//Instantiate the controlling class.
		controlFrame = new JFrame(CONTROL_TITLE);
		controlFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		//Create and set up the control pane.
		Mesh theGTW = new Mesh();

		Container contentPane = controlFrame.getContentPane();
		contentPane.add(theGTW.createOptionControls(),BorderLayout.CENTER);    

		controlFrame.pack();
		controlFrame.setLocationRelativeTo(null); //center it
		        
		// set up the image frame, but don't display it        
		imageFrame = new JFrame(IMAGE_TITLE);        
		imageLabel = new JLabel();        
		Container imageFrameContentPane = imageFrame.getContentPane();
		imageFrameContentPane.add(imageLabel,BorderLayout.CENTER);
		imageFrame.pack();        
        
        // initialize image        		
   		bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
   		bg = bi.createGraphics();
		
		transformNodes();
		segments = new Vector<ComplexNumber[]>();
		setSegments();
		drawMesh();
		
		// display the windows
		controlFrame.setVisible(true);
		showNewWindow();               
    }

	//Start the program
	public static void main(String[] args) 
	{		
		createAndShowGUI();
	}

}
