////////////////////////////////////////////////////////////////////////////////
//																			  //
// AUTHOR:      Tevfik Metin Sezgin	                        				  //
//				Massachusetts Institute of Technology						  //
//				Department of Electrical Engineering and Computer Science	  //
//				Artificial Intelligence Laboratory							  //
//																			  //
// E-MAIL:		mtsezgin@ai.mit.edu, mtsezgin@mit.edu						  //
//																			  //
// COPYRIGHT:   Tevfik Metin Sezgin                    						  //
//              All rights reserved. This code can not be copied, modified,   //
//              or distributed in whole or partially without the written 	  //
//				permission of the author. Also see the COPYRIGHT file.		  //
//																			  //
////////////////////////////////////////////////////////////////////////////////

  /**
  *
  * See the end of the file for the log of changes.
  *
  * $Author: mtsezgin $
  * $Date: 2000/06/08 03:20:07 $
  * $Revision: 1.18 $
  * $Headers$
  * $Id: Tablet.java,v 1.18 2000/06/08 03:20:07 mtsezgin Exp $
  * $Name:  $
  * $Locker:  $
  * $Source: /mit/mtsezgin/CVS/Repository/REC/Tablet.java,v $
  *
  **/


import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JToolBar;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.Toolkit;

import java.util.Vector;

import java.awt.geom.AffineTransform;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

/**
  * Acronyms: sdr -> StrokeDataReader, data -> StrokeData
  *
  * This class handles the updates to various frames.
  *
  **/

class      TabletGUIBehavior
extends    TabletDataProcessor
implements ActionListener
{
    public double a_scale       = 1000;
    public double v_scale       = 20;
    public double t_scale       = 0.1;
    public double theta_scale   = 10;
    public double d_theta_scale = 1000;
	
    public boolean display_lsq_vectors = false;
    public boolean show_parsed_objects = false;

    private Point previous_point;

    public Graphics v_window_graphics;
    public Graphics a_window_graphics;
    public Graphics d_window_graphics; 
    public Graphics fits_window_graphics; 
    public Graphics dd_dt_window_graphics;
    
    /**
    *
    * The constructor.
    *
    **/
    public TabletGUIBehavior( int     max_data_points, 
							  double  speed_average_scale, 
							  String  cursor_image_name,
							  boolean platform_is_windows9x,
							  boolean platform_is_windowsNT )
    {
        super( max_data_points,
			   speed_average_scale,
			   cursor_image_name,
			   platform_is_windows9x,
			   platform_is_windowsNT );

        setRecognitionMode( true );                
        v_menu.addActionListener( this );
        a_menu.addActionListener( this );
        d_menu.addActionListener( this );
        data_menu.addActionListener( this );
        fits_menu.addActionListener( this );
        dd_dt_menu.addActionListener( this );
        controls_menu.addActionListener( this );
        statistics_menu.addActionListener( this );
        open_menu.addActionListener( this );
        save_menu.addActionListener( this );
        save_as_menu.addActionListener( this );
        quit_menu.addActionListener( this );
        undo_menu.addActionListener( this );
        vectors_menu.addActionListener( this );
        display_menu.addActionListener( this );
		
		constructToolbar();
    }


    /**
    *
    * Construct the toolbar
    *
    **/
    public void
    constructToolbar() 
    {
		Panel bottom_panel = new Panel();
		bottom_panel.setLayout( new GridLayout( 0, 1 ) );
		bottom_panel.addKeyListener( object_manager );

		Action open_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/open.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
            object_manager.doOpen();
        	}
        };
        
		Action save_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/save.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
            object_manager.doSave();
        	}
        };
		
		Action reset_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/new.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
            	object_manager.reset();
		        clearScreen();
        	}
        };
		
		Action refresh_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/refresh.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
            	repaint();
        	}
        };
		
		Action toggle_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/toggle.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
				toggle();
        	}
        };
		
		Action undo_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/undo.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
        		object_manager.undo();
        	}
        };
		
		Action print_v_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/v.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
				x_list.setListData( Util.printToStringArray( data.time, 
															 "time" ) );
				y_list.setListData( Util.printToStringArray( data.speed, 
															 "speed" ) );
        	}
        };
		
		Action print_dv_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/dv.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
				x_list.setListData( Util.printToStringArray( data.time, 
															 "time" ) );
				y_list.setListData( Util.printToStringArray( data.acceleration, 
															 "a" ) );
        	}
        };
		
		Action print_d_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/d.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
				x_list.setListData( Util.printToStringArray( data.time, 
															 "time" ) );
				y_list.setListData( Util.printToStringArray( data.d, 
															 "d" ) );
        	}
        };
		
		Action print_dd_action = 
			new AbstractAction( null,
        		new ImageIcon("icons/dd.gif")) {
        	public void 
		    actionPerformed(ActionEvent e) {
				x_list.setListData( Util.printToStringArray( data.time, 
															 "time" ) );
				y_list.setListData( Util.printToStringArray( data.dd_dt, 
															 "dd_dt" ) );
        	}
        };
		
		JToolBar toolbar = new JToolBar();
		toolbar.add( open_action     );
		toolbar.add( save_action     );
		toolbar.add( undo_action     );
		toolbar.add( refresh_action  );
		toolbar.add( toggle_action   );
		toolbar.add( reset_action    );
		toolbar.add( print_v_action  );
		toolbar.add( print_dv_action );
		toolbar.add( print_d_action  );
		toolbar.add( print_dd_action );

		setLayout( new BorderLayout() );
		bottom_panel.add( status_bar  );
		bottom_panel.add( toolbar  );
		
		add( bottom_panel, BorderLayout.SOUTH );
    }

		
    /**
    *
    * Overloaded for double buffering.
    *
    **/
    public void
    paint( Graphics g )
    {
        update( g );
    }

     
    /**
    *
    * Handle action events
    *
    **/
    public void
    actionPerformed( ActionEvent e ) 
    {
        String command = e.getActionCommand();
        
        if ( command == "Quit" ) {
            System.exit( 0 );
        }
        
        if ( command == "Open" ) {
            object_manager.doOpen();
        }
        
        if ( command == "Save as" ) {
            object_manager.doSaveAs();
        }
        
        if ( command == "Save" ) {
            object_manager.doSave();
        }
        
        if ( command == "Undo" ) {
            object_manager.undo();
        }
        
        if ( command == "Reset" ) {
            object_manager.reset();
        }
		
        if ( command == "Refresh" ) {
			repaint();
        }
        
        if ( command == "Speed" ) {
            v_window.setVisible( !v_window.isVisible() );
            v_window_graphics = v_window.getGraphics();
            System.out.println( "Speed window." );
        } 
            
        if ( command == "Acceleration" ) {
            a_window.setVisible( !a_window.isVisible() );
            a_window_graphics = a_window.getGraphics();
            System.out.println( "Acceleration window." );
        }

        if ( command == "  \u0398" ) {
            d_window.setVisible( !d_window.isVisible() );
            d_window_graphics = d_window.getGraphics();
            System.out.println( "Direction window." );
        }

        if ( command == "Fits" ) {
            fits_window.setVisible( !fits_window.isVisible() );
            fits_window_graphics = fits_window.getGraphics();
            System.out.println( "fits window." );
        }
        
        if ( command == "d\u0398/dt" ) {
            dd_dt_window.setVisible( !dd_dt_window.isVisible() );
            dd_dt_window_graphics = dd_dt_window.getGraphics();
            System.out.println( "dd/dt window." );
        }
        
        if ( command == "Controls" ) {
            control_module.setVisible( 
                !control_module.isVisible() );
            System.out.println( "Controls module." );
        }
        
        if ( command == "Data" ) {
            data_window.setVisible( 
                !data_window.isVisible() );
            System.out.println( "Data_window." );
        }
        
        if ( command == "Statistics" ) {
            bar_statistics_module.setVisible( 
                !bar_statistics_module.isVisible() );
                
            dsw_statistics_module.setVisible( 
                !dsw_statistics_module.isVisible() );
				
            ddsw_statistics_module.setVisible( 
                !ddsw_statistics_module.isVisible() );
                
            sliding_window_statistics_module.setVisible( 
                !sliding_window_statistics_module.isVisible() );
                
            System.out.println( "Statistics window." );
        }
        
        if ( command == "Display LSQ Vectors" ) {
            display_lsq_vectors = !display_lsq_vectors;
            System.out.println( "Display LSQ Vectors set " + 
                                 display_lsq_vectors );
        }
        
        if ( command == "Show parsed objects" ) {
			toggle();
        }
    }

    
    /**
    *
    * toggle the state related with showing the parsed objects.
    *
    **/
    public void
    toggle()
    {
        show_parsed_objects = !show_parsed_objects;
        object_manager.show_parsed_objects = show_parsed_objects;
        clearScreen();
        object_manager.paint( getGraphics() );
        System.out.println( "Show parsed objects set " + 
                             show_parsed_objects );
	}


    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseReleased( MouseEvent e )
    {
		super.mouseReleased( e );
		update( getGraphics() );
    }

	
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseClicked( MouseEvent e ) 
    {
		super.mouseClicked( e );
    }

    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseEntered( MouseEvent e ) 
    {
		super.mouseEntered( e );
    }

    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseExited( MouseEvent e ) 
    {
		super.mouseExited( e );
    }

    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mousePressed( MouseEvent e ) 
    {
		super.mousePressed( e );
		previous_point = new Point( e.getPoint() );
    }
    

    /**
    *
    * Handle mouse motion events
    *
    **/
    public void
    mouseDragged( MouseEvent e ) 
    {
		super.mouseDragged( e );
        if ( !in_recognition_mode )
            return;
		
		Point point = new Point( e.getPoint() );
		
		Graphics g = getGraphics(); 
        g.setColor( Color.blue );
        g.drawLine( previous_point.x,
                    previous_point.y,
                    point.x,
                    point.y );
		previous_point = point;
    }


    /**
    *
    * Handle mouse motion events
    *
    **/
    public void
    mouseMoved( MouseEvent e ) 
    {
		super.mouseMoved( e );
    }
    
    
    /**
    *
    * Update various graphs and statistics.
    *
    **/
    public void 
    update( Graphics g )
    {
        clearScreen();
		initializeGraphicsObjects();

		if ( data != null ) {
			if ( v_window.isVisible() )
				GraphicsUtil.clearComponent( v_window     );
			if ( a_window.isVisible() )
				GraphicsUtil.clearComponent( a_window     );
			if ( d_window.isVisible() )
				GraphicsUtil.clearComponent( d_window     );
			if ( dd_dt_window.isVisible() )
				GraphicsUtil.clearComponent( dd_dt_window );

	        paintVelocity( data, Color.blue );
	        paintAcceleration( data, Color.blue );
        	paint_d( data, Color.blue );
	        paint_dd_dt( data, Color.blue );
		}
		
		update_fits();

		if ( dsw_statistics_module.isVisible() ) {
			dsw_statistics_module.plotSlidingWindowGraph( 
            	data.dd_dt,
                -.01,
                .01, 
                .0001,
                .001 );

			ddsw_statistics_module.plotBarGraph(
                data.dd_dt, 
                -Math.PI,
                Math.PI, 
                Math.PI/180 );
		}
        object_manager.paint(g);
    }


    /**
    *
    * Update the velocity graph.
    *
    **/
    public void
    paintVelocity( StrokeData data, Color color )
    {
        if ( !v_window.isVisible() ) {
            return;
		}
		
		AffineTransform affine_transform = new 
			AffineTransform( t_scale, 
							 0, 
							 0, 
							 -v_scale, 
							 40, 
							 v_window.getSize().height/2 );

		double speed[] = data.speed;
		long   time[]  = data.time;
		double average = data.averageSpeed();
		double arg0[]  = { time[0], time[time.length-1] };
		double arg1[]  = { average, average		        };
		paintFvsG( time, speed, color,      v_window, affine_transform );
		paintFvsG( arg0, arg1,  Color.red,  v_window, affine_transform );
    }


    /**
    *
    * Update the acceleration graph.
    *
    **/
    public void
    paintAcceleration( StrokeData data, Color color )
    {
        if ( !a_window.isVisible() ) {
            return;
		}
		
		AffineTransform affine_transform = new 
			AffineTransform( t_scale, 
							 0, 
							 0, 
							 -a_scale, 
							 40, 
							 a_window.getSize().height/2 );
		
		double acceleration[] = data.acceleration;
		long   time[]         = data.time;

		paintFvsG( time, acceleration, color, a_window, affine_transform );
    }

    
    /**
    *
    * Update the direction graph.
    *
    **/
    public void
    paint_d( StrokeData data, Color color )
    {
        if ( !d_window.isVisible() ) {
            return;
		}
		
		AffineTransform affine_transform = new 
			AffineTransform( t_scale, 
							 0, 
							 0, 
							 -theta_scale, 
							 40, 
							 d_window.getSize().height/2 );
		
		double d[]     = data.d;
		long   time[]  = data.time;
		double average = data.averageFinG( d, time );
		double arg0[]  = { time[0], time[time.length-1] };
		double arg1[]  = { average, average		        };
		paintFvsG( time, d,    color,      d_window, affine_transform );
		paintFvsG( arg0, arg1, Color.red,  d_window, affine_transform  );
    }


    /**
    *
    * Update change in direction
    *
    **/
    public void
    paint_dd_dt( StrokeData data, Color color )
    {
        if ( !dd_dt_window.isVisible() ) {
            return;
		}

		AffineTransform affine_transform = new 
			AffineTransform( t_scale, 
							 0, 
							 0, 
							 -d_theta_scale, 
							 40, 
							 dd_dt_window.getSize().height/2 );
							 
		double dd_dt[] = data.dd_dt;
		long   time[]  = data.time;
		
		double average = data.averageFinG( dd_dt, time );
		double arg0[] = { time[0], time[time.length-1] };
		double arg1[] = { average, average		       };
		paintFvsG( time, dd_dt, color,      dd_dt_window, affine_transform );
		paintFvsG( arg0, arg1,  Color.red,  dd_dt_window, affine_transform );
		double arg2[] = { -average, -average	       };
		paintFvsG( arg0, arg2,  Color.red,  dd_dt_window, affine_transform );
    }
	
	
    /**
    *
    * Paint f vs g in the frame using the input color and affine 
	* transform. f is on the horizontal axis.
    *
    **/
    public void
    paintFvsG( double          f[], 
			   double          g[], 
			   Color           color,
			   Component       component,
			   AffineTransform transform )
    {
		Graphics2D graphics = (Graphics2D)component.getGraphics();
		int        length   = ( f.length < g.length ) ? f.length : g.length;
		double     input[]  = new double[length*2];

		for ( int i=0; i<length; i++ ) {
			input[ i*2 ] = f[i];
			input[i*2+1] = g[i];
		}
		
		transform.transform( input, 0, input, 0, length );
        graphics.setColor( color );

        for ( int i=0; i<length-1; i++ ) {
            graphics.drawLine( (int)input[i*2],   (int)input[i*2+1],
							   (int)input[i*2+2], (int)input[i*2+3] );
        }
		
		// Axis
		int max_window_size = (int)( component.getSize().width/
									 transform.getShearX() +
							  		 component.getSize().height/
									 transform.getShearY() );
		input               = new double[10];
		
		input[0] = -max_window_size;
		input[1] = 0;
		
		input[2] = max_window_size;
		input[3] = 0;
		
		input[4] = 0;
		input[5] = 0;
		
		input[6] = 0;
		input[7] = max_window_size;
		
		input[8] = 0;
		input[9] = -max_window_size;
		
		transform.transform( input, 0, input, 0, 5 );

        graphics.setColor( Color.black );
		
        for ( int i=0; i<4; i++ ) {
            graphics.drawLine( (int)input[i*2],   (int)input[i*2+1],
							   (int)input[i*2+2], (int)input[i*2+3] );
        }
	}
	
	
    /**
    *
    * Paint f vs g in the frame using the input color and affine 
	* transform. f is on the horizontal axis.
    *
    **/
    public void
    paintFvsG( long            f[], 
			   double          g[], 
			   Color           color,
			   Component       component,
			   AffineTransform transform )
    {
		double f_copy[] = new double[f.length];
		
		for ( int i=0; i<f.length; i++ ) {
			f_copy[i] = (double)f[i];
		}
		paintFvsG( f_copy, g, color, component, transform );		
	}


    /**
    *
    * Update the info about the various fits (LSQE etc.)
    *
    **/
    public void
    update_fits()
    {
        int window_width  = fits_window.getSize().width;
        int window_height = fits_window.getSize().height;

        if ( ( Blackboard.hybrid_fits == null ) || 
		     ( fits_window_graphics   == null ) )
            return;
            
        fits_window_graphics.setColor( Color.white );
		GraphicsUtil.clearComponent( fits_window );
		
		int    length     = Blackboard.hybrid_fits.length;
		int    origin_x   = window_width/10;
		int    origin_y   = window_height*9/10;
		int    dx         = ((window_width-origin_x)/length)-1;
		int    max_height = window_height*5/10;
		double max_error  = Blackboard.hybrid_fits[length-1].getLSQError();

        fits_window_graphics.setColor( Color.black );
        fits_window_graphics.drawLine( 0,   origin_y, 
                                       500, origin_y );
        fits_window_graphics.setColor( Color.blue );
		for ( int i=0; i<length-1; i++ ) {
	        fits_window_graphics.drawLine( 
				origin_x + dx*i,   
				(int)(origin_y - Blackboard.hybrid_fits[i].getLSQError() * 
					max_height/max_error),
				origin_x + dx*(i+1), 
				(int)(origin_y - Blackboard.hybrid_fits[i+1].getLSQError() * 
					max_height/max_error) );
		}
    }


    /**
    *
    * Change the fit method, rederive the direction and related
	* entities. Also update the graphs.
    *
    **/
    public void
    setFitMehod( int method )
    {
		fit_method = method;
		
		data.setFitMethod( method );
		
		if ( graph_color == Color.blue ) {
			graph_color = Color.cyan;
		} else { 
			graph_color = Color.blue;
		}
		
		paint_d( data, graph_color );
		paint_dd_dt( data, graph_color );
    }

	
    /**
    * 
    * Do convolution.
    *
    **/
    public void
    convolveDirection()
    {
		double filter[] = new double[5];

		filter[0] = 1.0/16.0;
		filter[1] = 4.0/16.0;
		filter[2] = 6.0/16.0;
		filter[3] = 4.0/16.0;
		filter[4] = 1.0/16.0;
		
		data.convolveDirection( filter );
		
		if ( graph_color == Color.blue ) {
			graph_color = Color.cyan;
		} else { 
			graph_color = Color.blue;
		}
		paint_d( data, graph_color );
		paint_dd_dt( data, graph_color );
	}

	
    /**
    * 
    * Do convolution.
    *
    **/
    public void
    convolveChangeInDirection()
    {
		double filter[] = new double[5];

		filter[0] = 1.0/16.0;
		filter[1] = 4.0/16.0;
		filter[2] = 6.0/16.0;
		filter[3] = 4.0/16.0;
		filter[4] = 1.0/16.0;
		
		data.convolveChangeInDirection( filter );
		
		if ( graph_color == Color.blue ) {
			graph_color = Color.cyan;
		} else { 
			graph_color = Color.blue;
		}
		paint_dd_dt( data, graph_color );
	}


	
    /**
    * 
    * Zoom in all windows by k
    *
    **/
    public void
    zoom( double k )
    {
    	a_scale 	  *= k;
    	v_scale 	  *= k;
    	t_scale 	  *= k;
    	theta_scale   *= k;
    	d_theta_scale *= k;
	}
}

/**
  *
  * $Log: Tablet.java,v $
  *
  *
  **/
