// file: PhaseResponse.java
//

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

// include the necessary local files
//
//import Constants;
import graph.*;

// class: PhaseResponse
//
// this class implements a canvas which holds the phase
// response of a filter described by a set of poles and
// zeros.  
//
public class PhaseResponse extends G2Dint 
    implements ActionListener, Constants{
    
    // constant to define the sleep time of the thread
    // measured in milliseconds
    //
    
    public final static int SLEEP_TIME = 30;
    Timer timer;
    // set class variables
    //
    private double max_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;
    
    // array to hold the poles and zeros necessary to evaluate the filter
    //
    private PoleZero pz_array[];
    
    // data sets for the x and y-axes
    //
    DataSet x_data, y_data;
    
    // declare the array to hold the data
    //
    double data[];
    
    // declare the axes
    //
    Axis x_axis, y_axis;
    
    // variable for the type of response: analog/digital
    //
    private int design_type = Constants.DEFAULT_DESIGN;
    
    // components
    //
    JPanel panel = new JPanel();
    
    // set clear_flag
    //
    public boolean clear_flag = true;
    
    //default double values for xmax,xmin,xtic
    //
    double xmax = 0.5;
    double xmin = -0.5;
    double xtic = 0.25;
    double ymin = -3.14;
    double ymax = 3.14;
    double ytic = 0.75;
    
    //*****************************************************************
    //
    // constructor methods
    //
    //*****************************************************************
    
    // method: PhaseResponse
    //
    // parameters: 
    //     double frequency: (input) maximum frequency on the plot
    //     PoleZero pz_array_a[]: (input) pointer to the poles and zeros
    //     int type: (input) type of output: analog/digital
    // 
    // this method constructs a PhaseResponse canvas
    //
    public PhaseResponse(double frequency, PoleZero pz_array_a[], int type) {
	
	// instantiate the super-class
	//
	super();
	//title_string = new String("PhaseResponse");
	//xmin_string = new String("Freq Min");
	//xmax_string = new String("Freq Max");
	//xtic_string = new String("Freq Tic");
	//ymin_string = new String("Phase Min");
	//ymax_string = new String("Phase Max");
	//ytic_string = new String("Phase Tic");
	
	
	// initialize the frequency and set up a pointer to the
	// array of poles and zeros.
	//
	max_frequency = frequency;
	pz_array = pz_array_a;
	design_type = type;
	
	// remove all old datasets
	//
	this.detachDataSets();
	
	// initialize the parameters of this class
	//
	this.drawzero = false;
	this.drawgrid = true;
	this.borderTop = 20;
	this.borderLeft = 10;
	this.borderBottom = 3;
	this.borderRight = 40;
	this.framecolor = Color.black;
	this.gridcolor = Color.lightGray;
	
	// initiate the thread for this object
	//
	
	timer = new Timer(SLEEP_TIME, this);
	this.start();
	
    }
    public void actionPerformed(ActionEvent evt) {
	if (update_flag == true) {
	    // find the size of the display area in pixels
	    //
	    Dimension size = this.getSize();
	    
	    // get the width of the map
	    //
	    int width = size.width;
	    
	    // draw the frequency response due to the array of poles and zeros
	    //
	    if (width > 0) {
		this.detachDataSets();
		this.detachAxes();
		if (design_type == Constants.ANALOG) {
		    this.draw_analog_response(width);
		}
		else {
		    this.draw_digital_response(width);
		}
		
		repaint();
	    }
	    
	    // reset the update flag
	    //
	    update_flag = false;
	}
    }
    
    
    
    //****************************************************************
    //
    // private methods
    //
    //****************************************************************
    
    
    // method: draw_analog_response
    //
    // parameters:
    //     int length: (input) width of this window
    // 
    // this method draws the frequency response of the analog poles and zeros on
    // the screen
    //
    // returns: boolean
    //
    public boolean draw_analog_response(int length) {
	
	// declare local variables
	//
	double temp_value = 0.0;         // response value
	double freq_loc = -max_frequency;
	double freq_increment = (2 * max_frequency / length);
	
	// setup a data array to hold the output values and the pixel
	// representation of these
	//
	double data_array[] = new double[length << 1];    
	
	int loop_length = length << 1;
	
	for (int i = 0; i < loop_length; i+=2) {    
	    
	    // set the array value to the first calculated value
	    //
	    if (pz_array[0] != null) {
		temp_value = (pz_array[0]).phase_response(freq_loc);
	    }
	    else {
		temp_value = 0;
	    }
	    
	    // calculate the phase response due to each pole or zero
	    // and add it with the previous response
	    //
	    for (int j = 1; pz_array[j] != null; j++) {
		temp_value += (pz_array[j]).phase_response(freq_loc);
	    }
	    
	    // store the x_value to the array and increment the frequency
	    //
	    data_array[i] = freq_loc;      
	    
	    // convert to degrees
	    //
	    // Do not convert to degrees
	    //temp_value *= Constants.RAD_TO_DEG;
	    temp_value = temp_value * 1;
	    
	    
	    // assign the value to the data array
	    //
	    data_array[i+1] = temp_value;
	    
	    // increment frequency
	    //
	    freq_loc += freq_increment;      
	    
	}
	
	// attach the y_data to the graph
	//
	this.y_data = this.loadDataSet(data_array, length);
	
	// setup properties of the y_data
	//
	y_data.linestyle = 1;
	y_data.linecolor = Color.blue;    

	if(clear_flag) {
	    x_axis = this.createXAxis();
	    x_axis.setTitleText("Frequency (Hz)");
	    x_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN,8));
	    x_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default x-axis ranges and ticks
	    //
	    x_axis.setXMin( -max_frequency/2);
	    x_axis.setXMax( max_frequency/2);
	    x_axis.setXTic( max_frequency/4);
	    
	    // attach the data to the x-axis
	    //
	    x_axis.attachDataSet(y_data);
	    x_axis.resetXRange();
	    
	    // create the y-axis
	    //
	    y_axis = this.createYAxis();
	    y_axis.setTitleText("Phi(H(f))");
	    y_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN, 8));
	    y_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default y-axis ranges and ticks
	    //
	    y_axis.setYMin(-3.14);
	    y_axis.setYMax(3.14);
	    y_axis.setYTic(0.75);
	    
	    // attach data to this y-axis
	    //
	    y_axis.attachDataSet(y_data);
	    y_axis.resetYRange();
	    
	    //Set clear_flag to false value
	    //
	    clear_flag = false;
	}
	
	else {
	    x_axis = this.createXAxis();
	    x_axis.setTitleText("Frequency (Hz)");
	    x_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN,8));
	    x_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default x-axis ranges and ticks
	    //
	    x_axis.setXMin(xmin);
	    x_axis.setXMax(xmax);
	    x_axis.setXTic(xtic);
	    
	    // attach the data to the x-axis
	    //
	    x_axis.attachDataSet(y_data);
	    x_axis.resetXRange();
	    
	    // create the y-axis
	    //
	    y_axis = this.createYAxis();
	    y_axis.setTitleText("Phi(H(f))");
	    y_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN, 8));
	    y_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default y-axis ranges and ticks
	    //
	    y_axis.setYMin(ymin);
	    y_axis.setYMax(ymax);
	    y_axis.setYTic(ytic);
	    
	    // attach data to this y-axis
	    //
	    y_axis.attachDataSet(y_data);
	    y_axis.resetYRange();
	    
	}
	
	//}
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: draw_digital_response
    //
    // parameters:
    //     int length: (input) width of this window
    // 
    // this method draws the frequency response of the digital poles and zeros on
    // the screen
    //
    // returns: boolean
    //
    public boolean draw_digital_response(int length) {
	
	
	// declare local variables
	//
	double temp_value = 0.0;         // response value
	double freq_loc = -max_frequency/2;
	double freq_increment = (max_frequency / length);
	
	// setup a data array to hold the output values and the pixel
	// representation of these
	//
	double data_array[] = new double[length << 1];    
	
	int loop_length = length << 1;
	
	for (int i = 0; i < loop_length; i+=2) {    
	    
	    // set the array value to the first calculated value
	    //
	    if (pz_array[0] != null) {
		temp_value = (pz_array[0]).phase_response(freq_loc,
							  max_frequency);
	    }
	    else {
		temp_value = 0;
	    }
	    
	    // calculate the phase response due to each pole or zero
	    // and add it to the previous response
	    //
	    for (int j = 1; pz_array[j] != null; j++) {
		temp_value += (pz_array[j]).phase_response(freq_loc,
							   max_frequency);
	    }
	    
	    if(temp_value < -Math.PI) {
		while((temp_value < -Math.PI) || (temp_value > Math.PI)) {
		    temp_value = temp_value + (2.0 * Math.PI);
		}
	    }
	    
	    if(temp_value > Math.PI) {
		while((temp_value < -Math.PI) || (temp_value > Math.PI)) {
		    temp_value = temp_value - (2.0 * Math.PI);
		}
	    }  
	    
	    
	    // store the x_value to the array and increment the frequency
	    //
	    data_array[i] = freq_loc;      
	    freq_loc += freq_increment;      
	    
	    // convert to log scale
	    //
	    // Do not convert to degrees
	    //temp_value *= Constants.RAD_TO_DEG;
	    temp_value = temp_value * 1;
	    
	    // assign the value to the data array
	    //
	    data_array[i+1] = temp_value;
	}
	
	// attach the y_data to the graph
	//
	this.y_data = this.loadDataSet(data_array, length);
	
	// setup properties of the y_data
	//
	y_data.linestyle = 1;
	y_data.linecolor = Color.blue;    
	
	if(clear_flag) {
	    x_axis = this.createXAxis();
	    x_axis.setTitleText("Frequency (Hz)");
	    x_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN,8));
	    x_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default x-axis ranges and ticks
	    //
	    x_axis.setXMin( -max_frequency/2);
	    x_axis.setXMax( max_frequency/2);
	    x_axis.setXTic( max_frequency/4);
	    
	    // attach the data to the x-axis
	    //
	    x_axis.attachDataSet(y_data);
	    x_axis.resetXRange();
	    
	    // create the y-axis
	    //
	    y_axis = this.createYAxis();
	    y_axis.setTitleText("Phi(H(f))");
	    y_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN, 8));
	    y_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default y-axis ranges and ticks
	    //
	    y_axis.setYMin(-3.14);
	    y_axis.setYMax(3.14);
	    y_axis.setYTic(0.75);
	    
	    // attach data to this y-axis
	    //
	    y_axis.attachDataSet(y_data);
	    y_axis.resetYRange();
	    
	    //Set clear_flag to false value
	    //
	    clear_flag = false;
	}
	
	else {
	    x_axis = this.createXAxis();
	    x_axis.setTitleText("Frequency (Hz)");
	    x_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN,8));
	    x_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default x-axis ranges and ticks
	    //
	    x_axis.setXMin(xmin);
	    x_axis.setXMax(xmax);
	    x_axis.setXTic(xtic);
	    
	    // attach the data to the x-axis
	    //
	    x_axis.attachDataSet(y_data);
	    x_axis.resetXRange();
	    
	    // create the y-axis
	    //
	    y_axis = this.createYAxis();
	    y_axis.setTitleText("Phi(H(f))");
	    y_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN, 8));
	    y_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	    
	    // set the default y-axis ranges and ticks
	    //
	    y_axis.setYMin(ymin);
	    y_axis.setYMax(ymax);
	    y_axis.setYTic(ytic);
	    
	    // attach data to this y-axis
	    //
	    y_axis.attachDataSet(y_data);
	    y_axis.resetYRange();
	    
	}
	
	// exit gracefully
	//
	return true;
    }
    
    
    //****************************************************************
    //
    // public methods
    //
    //****************************************************************
    
    
    // method start
    //
    // parameters: none
    //
    // this method starts or restarts the threaded object
    //
    // returns: none
    //
    public void start () {
	update_flag = true;
	if(! timer.isRunning()) {
	    timer.start();
	}
    }
    
    
    // method stop
    //
    // parameters: none
    //
    // this method stops the threaded object
    //
    // returns: none
    //
    public void stop () {
	if(timer.isRunning()) {
	    timer.stop();
	}
	
	// set the applet thread to null
	//
	//    phase_resp_thread = null;
    }
    
    
    // method: set_pz_array
    //
    // parameters:
    //     PoleZero pz_array_a[]: (input) array of poles and zeros
    // 
    // this method sets the pz_array to point to the input variable
    //
    // returns: boolean
    //
    public boolean set_pz_array(PoleZero pz_array_a[]) {
	
	// set the pointer
	//
	pz_array = pz_array_a;
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: update_screen
    //
    // parameters: (none)
    // 
    // this method sets the update_flag causing a repaint of the
    // canvas
    //
    // returns: boolean
    //
    public boolean update_screen() {
	
	// set the update_flag
	//
	update_flag = true;
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    
    // method: set_design_type 
    //
    // parameters:
    //     integer new_type: (input) design type to change to
    //
    // this is a method to set the design type.  the design type will
    // be set to the value passed in to new_type
    //
    // returns: boolean
    //
    public boolean set_design_type(int new_type) {
	
	// set the type
	//
	design_type = new_type;
	
	// exit gracefully
	//
	return true;
    }
    
    // method: set_frequency
    //
    // parameters:
    //     double frequency: (input) frequency input
    // 
    // this method sets the sample frequency or maximum frequency
    //
    // returns: boolean
    //
    public boolean set_frequency(double frequency) {
	
	// set the frequency
	//
	max_frequency = frequency;
	
	// cause the screen to update
	//
	this.update_screen();
	
	// exit gracefully
	//
	return true;
	
    }

    // method: get_range
    // arguments:
    //   double x_max: get maximum x-axis value for the plot
    //   double x_min: get minimum x-axis value for the plot
    //   double x_tic: x-axis tick values
    //   double y_max: get maximum y-axis value for the plot
    //   double y_min: get minimum y-axis value for the plot
    //   double y_tic: y-axis tick values
    // returns: none
    // this method gets the x-axis and y-axis ranges for the plot
    //
    public void get_range(double x_max , double x_min, double x_tic,
			  double y_max , double y_min, double y_tic) {
	xmin = x_min;
	xmax = x_max;
	xtic = x_tic;
	ymin = y_min;
	ymax = y_max;
	ytic = y_tic;
	
    }
} // end class PhaseResponse
