// file: MagResponse.java
//

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

// class: MagResponse
//
// this class implements a canvas which holds the magnitude 
// response of a filter described by a set of poles and
// zeros.  
//
public class MagResponse 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 class variables
    //
    private double dc_gain = 1;
    
    // 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;
    
    //double values for xmax,xmin,xtic
    //
    double xmax = 0.5;
    double xmin = -0.5;
    double xtic = 0.25;
    double ymin = -40.0;
    double ymax = 40.0;
    double ytic = 10.0;
    
    //*****************************************************************
    //
    // constructor methods
    //
    //*****************************************************************
    
    // method: MagResponse
    //
    // 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 MagResponse canvas
    //
    public MagResponse(double frequency, PoleZero pz_array_a[], int type) {
        
        
        // instantiate the super-class
        //
        super();

        // 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 screen
    // 
    // this method draws the frequency response of the analog poles and zeros on
    // the screen
    //
    // returns: boolean
    //
    private boolean draw_analog_response(int length) {
        
        // declare local variables
        //
        double temp_value = 0.0;         // response value
        double freq_loc = -max_frequency;
        double data_array[];
        double freq_increment = (2 * max_frequency / length);
        
        // setup a data array to hold the output values and the pixel
        // representation of these
        //
        data_array = new double[length << 1];
        
        
        int loop_length = length << 1;
        for (int i = 0; i < loop_length; i+=2) {    
            
            // don't let this calculate the zero frequency point
            //
            if (Math.abs(freq_loc) < 1.0E-06)
                freq_loc += freq_increment;
            
            // set the array value to the first calculated value
            //
            if (pz_array[0] != null) {
                temp_value = (pz_array[0]).mag_response(freq_loc);
                if (Double.isInfinite(temp_value)) {
                    if (temp_value == Double.POSITIVE_INFINITY) {
                        temp_value = Double.MAX_VALUE;
                    }
                    else {
                        temp_value = -Double.MAX_VALUE;
                    }
                }
            }
            else {
                temp_value = 1;
            }
            
            // calculate the mag response due to each pole or zero
            // and multiply it with the previous response
            //
            for (int j = 1; pz_array[j] != null; j++) {
                temp_value *= (pz_array[j]).mag_response(freq_loc);
                
                // check for infinite values
                //
                if (Double.isInfinite(temp_value)) {
                    if (temp_value == Double.POSITIVE_INFINITY) {
                        temp_value = Double.MAX_VALUE;
                    }
                    else {
                        temp_value = -Double.MAX_VALUE;
                    }
                }
            }
            
            // convert to log scale
            //
            if (temp_value == 0.0) {
                temp_value = 20 * Math.log(Double.MIN_VALUE) / Constants.LOGE10;
            }
            else {
                temp_value = 20 * Math.log(dc_gain * temp_value) / Constants.LOGE10;
            }
            
            // store the x_value to the array and increment the frequency
            //
            data_array[i] = freq_loc;      
            data_array[i+1] = temp_value;
            
            // increment the 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.red;
	
	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("20*log10(|H(f)|)  (dB)");
	    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(-40.0);
	    y_axis.setYMax(40.0);
	    y_axis.setYTic(10);
	    
	    // 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("20*log10(|H(f)|)  (dB)");
	    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 screen
    // 
    // this method draws the frequency response of the digital poles and zeros on
    // the screen
    //
    // returns: boolean
    //
    private boolean draw_digital_response(int length) {
	
	// declare local variables
	//
	double temp_value = 0.0;         // response value
	double freq_loc = -max_frequency/2;  // current frequency to evaluate at
	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) {
	    
	    while (Math.abs(freq_loc) < 1.0E-06) {
		freq_loc += (0.0001 * freq_increment);
	    }
	    
	    // set the array value to the first calculated value
	    //
	    if (pz_array[0] != null) {
		temp_value = (pz_array[0]).mag_response(freq_loc, max_frequency);
		if (Double.isInfinite(temp_value)) {
		    if (temp_value == Double.POSITIVE_INFINITY) {
			temp_value = Double.MAX_VALUE;
		    }
		    else {
			temp_value = -Double.MAX_VALUE;
		    }
		}
	    }
	    else {
		temp_value = 1;
	    }
	    
	    // calculate the mag response due to each pole or zero
	    // and multiply it with the previous response
	    //
	    for (int j = 1; pz_array[j] != null; j++) {
		temp_value *= (pz_array[j]).mag_response(freq_loc, max_frequency);
		
		// check for infinite values
		//
		if (Double.isInfinite(temp_value)) {
		    if (temp_value == Double.POSITIVE_INFINITY) {
			temp_value = Double.MAX_VALUE;
		    }
		    else {
			temp_value = -Double.MAX_VALUE;
		    }
		}
	    }
	    
	    // store the x_value to the array and increment the frequency
	    //
	    data_array[i] = freq_loc;	
	    
	    // convert to log scale
	    //
	    if (temp_value == 0.0) {
		temp_value = -Double.MAX_VALUE;
	    }
	    else {
		temp_value = 20 * Math.log(dc_gain * temp_value) / Constants.LOGE10;
	    }
	    if (Double.isInfinite(temp_value)) {
		if (temp_value == Double.POSITIVE_INFINITY) {
		    temp_value = Double.MAX_VALUE;
		}
		else {
		    temp_value = -Double.MAX_VALUE;
		}
	    }      
	    
	    // assign the value to the data array
	    //
	    data_array[i+1] = temp_value;
	    
	    // increment the 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.red;
	
	
	
	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("20*log10(|H(f)|)  (dB)");
	    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(-40.0);
	    y_axis.setYMax(40.0);
	    y_axis.setYTic(10);
	    
	    // 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("20*log10(|H(f)|)  (dB)");
	    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 the canvas
	//
	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();
	}
    }
    
    
    // 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_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: set_dc_gain
    //
    // parameters:
    //     double new_dc_gain: (input) dc gain
    // 
    // this method sets the dc gain of the filter
    //
    // returns: boolean
    //
    public boolean set_dc_gain(double new_dc_gain) {
	
	// set the frequency in all of the sub-components
	//
	dc_gain = new_dc_gain;
	
	// update the screen
	//
	this.update_screen();
	
	// 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: 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: 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 MagResponse
