// file: Pzmap.java
//

// include the necessary system files
//
//import java.awt.*;
import java.lang.*;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Dimension;
import java.awt.event.*;


// include the necessary local files
//
//import Constants;

// class: Pzmap
//
// this class implements a canvas which holds either a digital or 
// analog pole-zero map and displays the location of poles and
// zeros on that map.
//
//public class Pzmap extends Canvas implements Runnable {
public class Pzmap extends JPanel 
    implements KeyListener, MouseListener, MouseMotionListener, Constants {
    
    // set class constants
    //
    public final static int SLEEP_TIME = 600;
    private Timer timer;
    
    
    // percentage margins to give the map from the edge of the frame
    //
    public final static double MAP_SPACING_FACTOR = 0.90; 
    
    // indicator of whether this is in analog or digital mode
    //
    private int map_type = Constants.DEFAULT_DESIGN;
    public double real_range = Constants.DEFAULT_REAL_RANGE;
    public double imag_range = Constants.DEFAULT_IMAG_RANGE;
    
    // array to hold the poles and zeros of this map
    //
    private PoleZero pz_array[];
    
    // dimensions of this object
    //
    private int height;
    private int width;
    private int half_height;
    private int half_width;
    private int radius;
    private int diameter;
    private int range_length;
    
    // array to hold the chart location of these poles and zeros
    //
    private PzGraphicsObj pz_graph_array[];
    
    // variable to hold the current total number of poles and zeros
    // combined
    //
    private int number_of_pz = 0;
    
    // variable to hold the current sample frequency for 
    // digital designs
    //
    private double sample_frequency = Constants.DEFAULT_SAMPLE_FREQ;
    
    // variable to hold the current sample frequency for 
    // digital designs
    //
    private double range_frequency = Constants.DEFAULT_SAMPLE_FREQ;
    
    // set up a thread to take care of updating the display
    //
    
    // set up a flag to determine if the display needs to be
    // updated and initialize it to false
    //
    boolean update_flag = false;  
    
    // output panel to control and data display panel to control
    //
    OutputPanel output;
    DataDisplayPanel data_display;
    
    // index of selected pole/zero
    //
    private int selected_index = -1;
    private boolean is_selected = false;
    
    // add a screen to display pz_map coordinates
    //
    private PzmapTracer tracer = new PzmapTracer("P/Z Map Coordinates");
    
    //*****************************************************************
    //
    // constructor methods
    //
    //*****************************************************************
    
    // method: Pzmap
    //
    // parameters: none
    // 
    // this method constructs a Pzmap of the default type
    //
    public Pzmap(int type, PoleZero pz_array_a[], OutputPanel output_a,
		 DataDisplayPanel data_display_a) {
	// call the super-class's constructor
	//
	super();
	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);
	// set the design_type
	//
	map_type = type;
	
	// set pointers to the output panel and the data display panel
	//
	output = output_a;
	data_display = data_display_a;
	
	// point to the array of poles and zeros and create the array
	// of pixel locations
	//
	pz_array = pz_array_a;
	pz_graph_array  = new PzGraphicsObj[pz_array.length];
	
	// cause manual repaint of the canvas
	//
	update_flag = true;
	
	// show tracer
	//
	timer = new Timer(SLEEP_TIME, new ActionListener() {
	    public void actionPerformed(ActionEvent evt) {
		// see if map needs updating, if so then call the paint method
		// then reset the flag
		//
		if (update_flag == true) {
		    repaint();
		    update_flag = false;
		}
	    }
	} );
	this.start();
    }
    
    
    //***************************************************************
    //
    // private methods
    //
    //***************************************************************
    
    
    // method: conjugate
    //
    // parameters: 
    //     int location: location of pole/zero to be conjugated
    //     
    // this method conjugates the currently selected pole or zero
    // if it has not already been conjugated
    //
    // returns: boolean
    //
    private boolean conjugate(int location) {
	
	// declare local variables
	//
	PoleZero current_pz = pz_array[location];
	PzGraphicsObj current_pz_graph = pz_graph_array[location];
	
	// flag the pole/zero as a conjugate
	//
	current_pz.conj_flag = true;
	current_pz_graph.conj_flag = true;
	
	// find the new pixel location for the conjugate of the
	// pole/zero graphic
	//
	int new_x = current_pz_graph.getx();
	int current_y = current_pz_graph.gety();
	int new_y = 0;
	
	// determine which side of the axis the current pole/zero is on and
	// place one on the other side.
	//
	if (half_height > current_y) {
	    int y_diff = half_height - current_y;
	    new_y = half_height + y_diff;
	}
	else if (half_height < current_y) {
	    int y_diff = current_y - half_height;
	    new_y = half_height - y_diff;
	}
	else {
	    new_y = current_y;
	}
	
	// set the coordinates of the conjugated pole/zero graphic
	//
	current_pz_graph.set_conj_limits(new_x - Constants.PZ_OFFSET, 
					 new_y - Constants.PZ_OFFSET, 
					 new_x + Constants.PZ_OFFSET, 
					 new_y + Constants.PZ_OFFSET);
	// update the data display panel
	//
	data_display.update(pz_array);
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method start
    //
    // parameters: none
    //
    // this method starts or restarts the threaded object
    //
    // returns: none
    //
    public void start () {
	
	if(! timer.isRunning()) {
	    timer.start();
	}
	update_flag = true;
	
    }
    
    
    // method stop
    //
    // parameters: none
    //
    // this method stops the threaded object
    //
    // returns: none
    //
    public void stop () {
	
	if( timer.isRunning()) {
	    timer.stop();
	}
	
    }
    
    
    // method: draw_analog_map
    //
    // parameters:
    //     Graphics g: graphics object to draw map on
    //
    // this method will draw an s-plane pole/zero map
    // on the input graphic object
    //
    // returns: boolean
    //
    private boolean draw_analog_map(Graphics graph) {
	
	// declare local variables
	//
	int axes_length;                   // length of axes
	int spacing;                       // spacing from edges
	int width_offset, height_offset;   // offsets for axes
	
	
	// determine length of axes
	//
	if (height < width) {
	    axes_length = height;
	}
	else {
	    axes_length = width;
	}
	
	// determine the range in pixels
	//
	range_length = axes_length >> 1;
	
	// determine the offsets for the edge of the axes
	//
	width_offset = (width - axes_length) >> 1;
	height_offset = (height - axes_length) >> 1;
	
	// draw the line making up the x-axis
	//
	graph.drawLine(half_width, height_offset, half_width, 
		       height - height_offset);
	
	// draw the line making up the y-axis
	//
	graph.drawLine(width_offset, half_height, width - width_offset, 
		       half_height);
	
	// place all of the poles and zeros on the map
	//
	for (int i = 0; i < this.number_of_pz; i++) {
	    
	    // draw the poles or zeros
	    //
	    (pz_graph_array[i]).draw_self(graph);
	}
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: draw_digital_map
    //
    // parameters:
    //     Graphics g: graphics object to draw map on
    //
    // this method will draw a unit circle pole/zero map
    // on the input graphic object
    //
    // returns: boolean
    //
    private boolean draw_digital_map(Graphics graph) {
	
	// declare local variables
	//
	int corner_x, corner_y;            // corner of unit circle
	int spacing;                       // spacing from edges
	int axes_length;                   // length of axes
	int width_offset, height_offset;   // offsets for axes
	
	
	// determine the diameter of the unit circle such that it is less than 
	// the minimum dimension including buffer space.  also determine the 
	// spacing necessary to keep the circle off of the edges of the map.
	// also determine the length of the axes.
	//
	if (height < width) {
	    diameter = (int) (height * MAP_SPACING_FACTOR);
	    radius = diameter >> 1;
	    spacing = half_height - radius;
	    axes_length = height;
	}
	else {
	    diameter = (int) (width * MAP_SPACING_FACTOR);
	    radius = diameter >> 1;
	    spacing = half_width - radius;
	    axes_length = width;
	}
	
	// determine the offsets for the edge of the axes
	//
	width_offset = (width - axes_length) >> 1;
	height_offset = (height - axes_length) >> 1;
	
	// draw the unit circle
	//
	// determine offsets for the corner of the circle
	//
	corner_x = half_width - radius;
	corner_y = half_height - radius;
	graph.drawOval(corner_x, corner_y, diameter, diameter);
	
	// draw the unit circle increments
	//
	graph.setColor(Color.lightGray);
	for (double div_factor = 0.2; div_factor <= 0.8; div_factor+= 0.2) {
	    
	    // determine offsets for the corner of the circle
	    //
	    corner_x = half_width - (int) (div_factor * radius);
	    corner_y = half_height - (int) (div_factor * radius);
	    graph.drawOval(corner_x, corner_y, (int) (div_factor * diameter),
			   (int) (div_factor * diameter));
	}
	
	graph.setColor(Color.black);
	
	
	// draw the line making up the y-axis
	//
	graph.drawLine(half_width, height_offset, half_width, 
		       height - height_offset);
	
	// draw the line making up the x-axis
	//
	graph.drawLine(width_offset, half_height, width - width_offset, 
		       half_height);
	
	// draw lines at 45 degree increments
	//
	graph.setColor(Color.lightGray);
	
	// get the x,y coords of the lines.
	//
	double angle = Math.PI / 4;
	int zerox = half_width;
	int zeroy = half_height;    
	int x_coord = (int) (radius * Math.cos(angle));
	int y_coord = (int) (radius * Math.sin(angle));    
	graph.drawLine(zerox - x_coord, half_height - y_coord,
		       zerox + x_coord, half_height + y_coord);
	graph.drawLine(zerox - x_coord, half_height + y_coord,
		       zerox + x_coord, half_height - y_coord);        
	
	graph.setColor(Color.black);    
	
	// draw all of the poles and zeros on the map
	//
	for (int i = 0; i < this.number_of_pz; i++) {
	    
	    // draw the poles or zeros
	    //
	    (pz_graph_array[i]).draw_self(graph);
	}
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: add_analog_pole
    //
    // parameters:
    //     double real: (input) the real portion of an analog pole
    //     double imag: (input) the imag portion of an analog pole
    //
    // this is a method to add a pole to the current analog map 
    //
    // returns: boolean
    //  
    private boolean add_analog_pole (double frequency, double bandwidth) {
	
	// create a new pole at the next position of the
	// array and increment the counter
	//
	pz_array[this.number_of_pz] = new PoleZero(Constants.ANALOG,
						   Constants.POLE_INDICATOR, 
						   frequency, bandwidth);
	
	// determine the location on the map of the
	// pole or zero
	//
	int location_x = half_width + 
	    (int) ((-pz_array[number_of_pz].bandwidth / 2 * range_length)/real_range);
	int location_y = half_height - 
	    (int) ((pz_array[number_of_pz].frequency * range_length)/imag_range);
	
	// put the location of the pole or zero in the object array
	// for later use
	//
	pz_graph_array[number_of_pz] = new PzGraphicsObj(
							 location_x - Constants.PZ_OFFSET, 
							 location_y - Constants.PZ_OFFSET, 
							 location_x + Constants.PZ_OFFSET, 
							 location_y + Constants.PZ_OFFSET,
							 Constants.POLE_INDICATOR);
	
	// increment the number of poles and zeros
	//
	this.number_of_pz++; 
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: add_analog_zero
    //
    // parameters:
    //     double real: (input) the real portion of an analog zero 
    //     double imag: (input) the imag portion of an analog zero 
    //
    // this is a method to add a zero to the current analog map 
    //
    // returns: boolean
    //
    private boolean add_analog_zero (double frequency, double bandwidth) {
	
	// create a new zero at the next position of the
	// array and increment the counter
	//
	pz_array[this.number_of_pz] = new PoleZero(Constants.ANALOG, 
						   Constants.ZERO_INDICATOR, 
						   frequency, bandwidth);
	
	// determine the location on the map of the
	// pole or zero
	//
	int location_x = half_width + 
	    (int) ((-pz_array[number_of_pz].bandwidth / 2 * range_length) /
		   real_range);
	int location_y = half_height - 
	    (int) ((pz_array[number_of_pz].frequency * range_length)/imag_range);
	
	// put the location of the pole or zero in the object array
	// for later use
	//
	pz_graph_array[number_of_pz] = new PzGraphicsObj(
							 location_x - Constants.PZ_OFFSET, 
							 location_y - Constants.PZ_OFFSET, 
							 location_x + Constants.PZ_OFFSET, 
							 location_y + Constants.PZ_OFFSET,
							 Constants.ZERO_INDICATOR);
	
	// increment the number of poles and zeros
	//
	this.number_of_pz++; 
	
	// 
	// exit gracefully
	//
	return true;
	
    }
    
    // method: add_digital_pole
    //
    // parameters:
    //     double frequency_a: (input) the frequency portion of a digital pole
    //     double bandwidth_a: (input) the bandwidth portion of a digital pole
    //
    // this is a method to add a pole to the current digital map 
    //
    // returns: boolean
    //
    private boolean add_digital_pole(double frequency_a, double bandwidth_a) {
	
	// create a new pole at the next position of the
	// array and increment the counter
	//
	// create a new pole or zero at the next position of the
	// array and increment the counter
	//
	pz_array[this.number_of_pz] = new PoleZero(Constants.DIGITAL,
						   Constants.POLE_INDICATOR,
						   frequency_a, bandwidth_a);
	
	// determine the location on the map of the
	// pole or zero
	//
	pz_array[this.number_of_pz].z_real = ZPlane.get_real(frequency_a,
							     bandwidth_a,
							     sample_frequency);
	pz_array[this.number_of_pz].z_imag = ZPlane.get_imag(frequency_a,
							     bandwidth_a,
							     sample_frequency);
	int location_x = half_width + (int) (pz_array[number_of_pz].z_real * 
					     radius);
	int location_y = half_height - (int) (pz_array[number_of_pz].z_imag *
					      radius);
	
	// put the pole or zero on the map
	//
	// put the location of the pole or zero in the object array
	// for later use
	//
	pz_graph_array[number_of_pz] = new PzGraphicsObj(
							 location_x - Constants.PZ_OFFSET, 
							 location_y - Constants.PZ_OFFSET, 
							 location_x + Constants.PZ_OFFSET, 
							 location_y + Constants.PZ_OFFSET,
							 Constants.POLE_INDICATOR);
	
	// increment the number of poles and zeros
	//
	this.number_of_pz++; 
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: add_digital_zero
    //
    // parameters:
    //     double z_real: (input) the real portion of a digital zero in the
    //                            z-plane
    //     double z_imag: (input) the imag portion of a digital zero in the
    //                            z-plane
    //
    // this is a method to add a zero to the current digital map 
    //
    // returns: boolean
    //
    private boolean add_digital_zero(double frequency_a, double bandwidth_a) {
	
	// create a new pole or zero at the next position of the
	// array and increment the counter
	//
	pz_array[this.number_of_pz] = new PoleZero(Constants.DIGITAL,
						   Constants.ZERO_INDICATOR,
						   frequency_a, bandwidth_a);
	// determine the location on the map of the
	// pole or zero
	//
	pz_array[this.number_of_pz].z_real = ZPlane.get_real(frequency_a,
							     bandwidth_a,
							     sample_frequency);
	pz_array[this.number_of_pz].z_imag = ZPlane.get_imag(frequency_a,
							     bandwidth_a,
							     sample_frequency);
	int location_x = half_width + (int) (pz_array[number_of_pz].z_real * 
					     radius);
	int location_y = half_height - (int) (pz_array[number_of_pz].z_imag *
					      radius);
	
	// put the pole or zero on the map
	//
	// put the location of the pole or zero in the object array
	// for later use
	//
	pz_graph_array[number_of_pz] = new PzGraphicsObj(
							 location_x - Constants.PZ_OFFSET, 
							 location_y - Constants.PZ_OFFSET, 
							 location_x + Constants.PZ_OFFSET, 
							 location_y + Constants.PZ_OFFSET,
							 Constants.ZERO_INDICATOR);
	
	// increment the number of poles and zeros
	//
	this.number_of_pz++; 
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    //****************************************************************
    //
    // public methods
    //
    //****************************************************************
    
    
    // method: add_pole
    //
    // parameters:
    //     double frequency: (input) the frequency of a pole
    //     double bandwidth: (input) bandwidth of a pole
    //
    // this is a method to add a pole to the current map 
    //
    // returns: boolean
    //
    public boolean add_pole (double frequency, double bandwidth) {
	
	// if we are currently using a  analog map then add an analog
	// pole, else add a digital pole
	//
	if (this.map_type == Constants.ANALOG) { 
	    add_analog_pole(frequency, bandwidth);
	}
	else {
	    add_digital_pole(frequency, bandwidth);
	}
	
	// cause a refresh of the screen on the next thread cycle
	//
	update_flag = true;
	
	// exit gracefully
	//
	return true;
    }
    
    // method: add_zero
    //
    // parameters:
    //     double frequency: (input) the frequency of a zero
    //     double bandwidth: (input) the bandwidth of a digital zero
    //
    // this is a method to add a zero to the current map 
    //
    // returns: boolean
    //
    public boolean add_zero (double frequency, double bandwidth) {
	
	// if we are currently using a  analog map then add an analog
	// zero, else add a digital zero
	//
	if (this.map_type == Constants.ANALOG) { 
	    add_analog_zero(frequency, bandwidth);
	}
	else {
	    add_digital_zero(frequency, bandwidth);
	}
	
	// cause a refresh of the screen on the next thread cycle
	//
	update_flag = true;
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: set_sample_frequency
    //
    // parameters:
    //     double new_freq: (input) sample frequency to change to
    //
    // this is a method to set the sample frequency.  This has an
    // effect only on the digital map
    //
    // returns: boolean
    //
    public boolean set_sample_frequency (double new_freq) {
	
	// make sure the sample frequency is in the proper range
	//
	if (0.0 < new_freq) {
	    
	    // set the sample frequency to the input frequency and
	    // reset the graphics
	    //
	    this.sample_frequency = new_freq;
	    this.real_range = new_freq;
	    this.imag_range = new_freq;
	    
	    // reset the limits for all of the poles and zeros
	    //
	    if (this.map_type == Constants.ANALOG) {
		for (int i = 0; i < this.number_of_pz; i++) {
		    
		    // determine the location on the map of the
		    // pole or zero
		    //
		    int location_x = half_width + 
			(int) ((-pz_array[i].bandwidth / 2 * range_length)/real_range);
		    int location_y = half_height - 
			(int) ((pz_array[i].frequency * range_length)/imag_range);
		    
		    // put the location of the pole or zero in the object array
		    // for later use
		    //
		    (pz_graph_array[i]).set_limits(location_x - Constants.PZ_OFFSET, 
						   location_y - Constants.PZ_OFFSET, 
						   location_x + Constants.PZ_OFFSET, 
						   location_y + Constants.PZ_OFFSET);
		    
		    // re-conjugate the graph object if necessary
		    //
		    if (pz_graph_array[i].is_conjugate()) {
			this.conjugate(i);
		    }
		}
	    }
	    else {
		
		// declare temp space so we don't have to constantly compute
		// array locations internally
		//
		PoleZero temp_pz;
		
		// for each pole or zero add to the display area
		//
		for (int i = 0; i < this.number_of_pz; i++) {
		    
		    // assign the next array location to temp
		    //
		    temp_pz = pz_array[i];
		    
		    // add the new freq/bandwidth pairs
		    //
		    temp_pz.z_real = ZPlane.get_real(temp_pz.frequency, 
						     temp_pz.bandwidth,
						     sample_frequency);
		    temp_pz.z_imag = ZPlane.get_imag(temp_pz.frequency, 
						     temp_pz.bandwidth,
						     sample_frequency);
		    
		    // determine the new location on the pz-map
		    //
		    int location_x = half_width + (int) (pz_array[i].z_real * 
							 radius);
		    int location_y = half_height - (int) (pz_array[i].z_imag *
							  radius);
		    pz_graph_array[i].set_limits(location_x - Constants.PZ_OFFSET, 
						 location_y - Constants.PZ_OFFSET, 
						 location_x + Constants.PZ_OFFSET, 
						 location_y + Constants.PZ_OFFSET);
		    
		    // re-conjugate the graph object if necessary
		    //
		    if (pz_graph_array[i].is_conjugate()) {
			this.conjugate(i);
		    }
		}
	    }
	    
	    
	    // cause the map to update
	    //
	    update_flag = true;
            
	    // exit gracefully
	    //
	    return true;
	}
	else {
	    
	    // exit with error
	    //
	    return false;
	}
    }
    
    
    // method: set_map_type 
    //
    // parameters:
    //     int new_type: (input) map type to change to
    //
    // this is a method to set the map type.  the map type will
    // be set to the value passed in to new_type
    //
    // returns: boolean
    //
    public boolean set_map_type(int new_type) {
	
	// make sure the new_type is valid, then set the map_type
	// variable, and return a boolean value which indicates whether
	// or not the new_type was valid.  note:  this assumes that 
	// the map_type is currently a valid value
	//
	if((new_type == Constants.DIGITAL) || (new_type == Constants.ANALOG)) {
	    this.map_type = new_type;
	    this.clear_all();
	    
	    // cause a repaint of the screen on the next thread cycle
	    //
	    update_flag = true;
	    
	    // exit gracefully
	    //
	    return true;
	}
	else {
	    
	    // exit with error
	    //
	    return false;
	}
    }
    
    
    // method: clear_all
    //
    // parameters:
    //
    // this is a method to clear all poles and zeros from the map
    //
    // returns: boolean
    //
    public boolean clear_all() {
	
	// set all array positions to null
	//
	for (int i = 0; i < number_of_pz; i++) {
	    pz_array[i] = null;
	}
	// clear the map by setting the number of poles and zeros to 0
	//
	this.number_of_pz = 0;
	
	// cause a repaint of the screen on the next thread cycle
	//
	update_flag = true;
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: paint
    //
    // parameters:
    //     Graphics g: graphics object to be updated
    //
    // this method will repaint the Pzmap on the screen.
    // if map_type is set to DIGITAL then a unit circle type
    // map will be drawn. if it is set to ANALOG, a s-plane
    // map will be drawn.
    //
    // returns: none
    //
    public void paintComponent(Graphics g) {
	super.paintComponent(g);
	// find the size of the map in pixels
	//
	Dimension size = this.getSize();
	
	// get the width and height of the map
	//
	height = size.height;
	width = size.width;
	half_height = height >> 1;
	half_width = width >> 1;
	
	// check for map type then display appropriate type
	//
	if (map_type == Constants.DIGITAL) {
	    draw_digital_map(g);
	}
	else {
	    draw_analog_map(g);
	}
	
    }
    
    
    //*****************************************************************
    //
    // event handler methods
    //
    //*****************************************************************
    
    
    // method: keyDown
    //
    // parameters: 
    //     Event event: (input) an Event object indicating the event to handle
    //     int key: (input) key which was released
    //
    // this method determines the key pressed
    // if the key is an arrow key then it allows micro-positioning
    //
    // returns: boolean
    //
    
    
    public void keyTyped(KeyEvent e) {
	;
    }
    
    //  public boolean keyDown(Event e, int key) {
    public void keyPressed(KeyEvent e) {
	
	// declare local variables
	//
	double new_freq = 0.0, new_band = 0.0;
	
	char key = e.getKeyChar();
	
	if (key == 't') {
	    
	    // show the tracer window.
	    //
	    tracer.setVisible(true);
	}
	if (key == 'h') {
	    tracer.setVisible(false);
	}
	// determine if a pole or zero is selected
	//
	if (is_selected) {
	    
	    // if selected then handle the key press by moving that pole or
	    // zero by one pixel in the appropriate direction
	    //
	    switch (key) {
	    case 'i':
	    case 'I':
	    case 'k':
	    case 'K':
	    case 'j':
	    case 'J':
	    case 'l':
	    case 'L':		
		
		// update the graphics handler associated with that pole
		// or zero
		//
		PzGraphicsObj pz_graphics = pz_graph_array[selected_index];
		pz_graphics.move_by_one(key);
		
		// get the new x and y values
		//
		int new_x = pz_graphics.getx();
		int new_y = pz_graphics.gety();
		
		// update the pole/zero array 
		//
		if (map_type == Constants.ANALOG) {
		    new_band = -2 * ((new_x - half_width) * real_range) 
			/ range_length;
		    (pz_array[selected_index]).bandwidth = new_band;
		    new_freq = ((half_height - new_y) * imag_range) 
			/ range_length;
		    (pz_array[selected_index]).frequency = new_freq;
		}
		
		// digital update
		//
		else {
		    double z_real = (double) (new_x - half_width) / radius;
		    (pz_array[selected_index]).z_real = z_real;
		    double z_imag = (double) (half_height - new_y) / radius;
		    (pz_array[selected_index]).z_imag = z_imag;
		    new_freq = ZPlane.get_freq(z_real, z_imag, 
					       sample_frequency);
		    //(pz_array[selected_index]).bandwidth = new_band;
		    (pz_array[selected_index]).frequency = new_freq;
		    new_band = ZPlane.get_band(z_real, z_imag, 
					       sample_frequency);
		    (pz_array[selected_index]).bandwidth = new_band;
		}
		
		// update the trace value
		//
		tracer.set_both(new_freq, new_band);
		
		// update the output screen and
		// the value in the display screen
		//
		data_display.update(pz_array);
		output.update_screen();
		update_flag = true;
		
	    } // end switch
	}
    }
    
    
    
    // method: keyUp
    //
    // parameters: 
    //     Event event: (input) an Event object indicating the event to handle
    //     int key: (input) key which was released
    //
    // this method determines the key released
    // if the key is an arrow key then it refreshes the output screen
    //
    // returns: boolean
    //
    //  public boolean keyUp(Event e, int key) {
    public void keyReleased(KeyEvent e) {
	char key = e.getKeyChar();
	
	// determine if a pole or zero is selected
	//
	if (is_selected) {
	    
	    // if selected then handle the key press by moving that pole or
	    // zero by one pixel in the appropriate direction
	    //
	    switch (key) {
	    case 'i':
	    case 'I':	   
	    case 'k':
	    case 'K':	   
	    case 'j':
	    case 'J':	   
	    case 'l':
	    case 'L':	   
		output.update_screen();
		update_flag = true;
		break;
		
		// user wants to conjugate the current pole/zero
		//
	    case 'c':
	    case 'C':	   
		this.conjugate(selected_index);
		this.update_flag = true;
		output.update_screen();
		break;
		
		// user wants to delete this pole or zero
		//
	    case 'd':
	    case 'D':	   
		
		// delete the selected pole/zero
		//
		pz_array[selected_index] = null;
		pz_graph_array[selected_index] = null;
		
		// move the remaining poles and zeros down by one
		//
		for (int i = selected_index, j = i+1; 
		     j < number_of_pz; i++, j++) {
		    pz_array[i] = pz_array[j];
		    pz_array[j] = null;	  
		    pz_graph_array[i] = pz_graph_array[j];
		    pz_graph_array[j] = null;
		}
		
		// set the new number of poles/zeros
		//
		number_of_pz--;
		is_selected = false;
		selected_index = -1;
		
		// update the screen
		//
		update_flag = true;
		output.update_screen();
		if( number_of_pz == 0) {
		    output.impulse_response.clear_flag = true;
		}
		data_display.update(pz_array);
		break;
	    }
	}
    }
    
    public void mouseClicked(MouseEvent e){};
    public void mouseEntered(MouseEvent e){};
    
    // method: mouseMove
    //
    // parameters: 
    //     Event event: (input) an Event object indicating the event to handle
    //     int x: (input) x location of mouse click
    //     int y: (input) x location of mouse click
    //
    // this method determines the location of a mouse movement
    //
    // returns: boolean
    //
    
    public void mouseMoved (MouseEvent e) {
	int x = e.getX();
	int y = e.getY();
	
	// declare local variables
	//
	double new_freq, new_band;
	//System.out.println("-->Moved x="+x+"y="+y);	
	// analog update
	//
	if (map_type == Constants.ANALOG) {
	    new_band = -2 * ((x - half_width) * real_range) / range_length;
	    new_freq = ((half_height - y) * imag_range) / range_length;
	}
	
	// digital update
	//
	else {
	    double z_real = (double) (x - half_width) / radius;
	    double z_imag = (double) (half_height - y) / radius;
	    new_freq = ZPlane.get_freq(z_real, z_imag, sample_frequency);
	    new_band = ZPlane.get_band(z_real, z_imag, sample_frequency);
	}
	
	// update the tracer
	//
	tracer.set_both(new_freq, new_band);
    }
    
    
    // method: mouseDown
    //
    // parameters: 
    //     Event event: (input) an Event object indicating the event to handle
    //     int x: (input) x location of mouse click
    //     int y: (input) x location of mouse click
    //
    // this method determines the location of a mouse click and figures out
    // whether or not it was intended to be on a pole or zero.  It sets the
    // current pole or zero to be the one clicked on.
    //
    // returns: boolean
    //
    public void mousePressed(MouseEvent e) {
	int x = e.getX();
	int y = e.getY();
	//System.out.println("-->Pressed x="+x+"y="+y);
	// declare local variables
	//
	double new_freq;
	double new_band;
	
	// reset indicators to show that no pole/zero is currently selected
	//
	is_selected = false;
	selected_index = -1;
	
	// search for the pole or zero that may have been selected
	//
	for (int i = 0; (is_selected == false) && (i < number_of_pz); i++) {
	    
	    // if found then make that one the currently selected pole or zero
	    //
	    if ((pz_graph_array[i]).does_contain(x,y)) {
		is_selected = true;
		selected_index = i;
		data_display.select_item((pz_array[i]).frequency,
					 (pz_array[i]).bandwidth,
					 (pz_array[i]).pz_indicator);
		
		// update the trace value
		//
		tracer.set_both((pz_array[i]).frequency, 
				(pz_array[i]).bandwidth);
		
	    }
	}
	
	// reset the data_display to be unselected
	//
	if (is_selected == false) {
	    
	    // analog update
	    //
	    if (map_type == Constants.ANALOG) {
		new_band = -2 * ((x - half_width) * real_range) / range_length;
		new_freq = ((half_height - y) * imag_range) / range_length;
	    }
	    
	    // digital update
	    //
	    else {
		double z_real = (double) (x - half_width) / radius;
		double z_imag = (double) (half_height - y) / radius;
		new_freq = ZPlane.get_freq(z_real, z_imag, sample_frequency);
		new_band = ZPlane.get_band(z_real, z_imag, sample_frequency);
	    }
	    
	    // update the tracer
	    //
	    tracer.set_both(new_freq, new_band);
	    
	    data_display.reset_selected();
	}    
    }
    
    
    // method: mouseUp
    //
    // parameters: 
    //     Event event: (input) an Event object indicating the event to handle
    //     int x: (input) x location of mouse click
    //     int y: (input) x location of mouse click
    //
    // this method determines the location of a mouse click
    // it sets the current pole or zero to be unknown
    // returns: boolean
    //
    //  public boolean mouseUp(Event e, int x, int y) {
    public void mouseReleased(MouseEvent e) {
	int x = e.getX();
	int y = e.getY();
	
	// update the entire output panel
	//
	if (is_selected) {
	    (pz_graph_array[selected_index]).does_contain(x, y);
	    output.update_screen();
	}
	
	update_flag = true;
	
	// get keyboard for the pz_map
	//
	this.requestFocus();    
    }
    
    
    // method: mouseDrag
    //
    // parameters: 
    //     Event event: (input) an Event object indicating the event to handle
    //     int x: (input) x location of mouse
    //     int y: (input) x location of mouse
    //
    // this method determines the location of a mouse drag and
    // sets the current pole or zero to point to this new location
    //
    // returns: boolean
    //
    public void mouseDragged(MouseEvent e) {
	
	int x = e.getX();
	int y = e.getY();
	// declare local variables
	//
	double new_freq = 0.0;
	double new_band = 0.0;
	double old_freq = 0.0;
	double old_band = 0.0;
	int pz_type = 0;
	
	// if there is a pole or zero selected then move it and update
	// the value
	//
	if (is_selected) {
	    
	    (pz_graph_array[selected_index]).set_limits(x - Constants.PZ_OFFSET, 
							y - Constants.PZ_OFFSET, 
							x + Constants.PZ_OFFSET, 
							y + Constants.PZ_OFFSET);
	    
	    if ((pz_graph_array[selected_index]).is_conjugate()) {
		this.conjugate(selected_index);
		(pz_graph_array[selected_index]).does_contain(x,y);
	    }
	    
	    // analog update
	    //
	    if (map_type == Constants.ANALOG) {
		new_band = -2 * ((x - half_width) * real_range) / range_length;
		(pz_array[selected_index]).bandwidth = new_band;
		new_freq = ((half_height - y) * imag_range) / range_length;
		(pz_array[selected_index]).frequency = new_freq;
	    }
	    
	    // digital update
	    //
	    else {
		double z_real = (double) (x - half_width) / radius;
		double z_imag = (double) (half_height - y) / radius;
		(pz_array[selected_index]).z_real = z_real;
		(pz_array[selected_index]).z_imag = z_imag;
		new_freq = ZPlane.get_freq(z_real, z_imag, sample_frequency);
		(pz_array[selected_index]).frequency = new_freq;
		new_band = ZPlane.get_band(z_real, z_imag, sample_frequency);
		(pz_array[selected_index]).bandwidth = new_band;
	    }
	    
	    // update the tracer
	    //
	    tracer.set_both(new_freq, new_band);
	    
	    // update part of the output screen and
	    // the value in the display screen
	    //
	    output.update_screen();
	    data_display.update(pz_array);
	    update_flag = true;
	}
    }
    
    public void mouseExited(MouseEvent e) {
	;
    }
    
} // end class Pzmap
