/** 
  * 
  * See the end of the file for the log of changes.
  * 
  * $Author: mtsezgin $
  * $Date: 2000/06/03 01:52:13 $   
  * $Revision: 1.13 $
  * $Headers$
  * $Id: ObjectManager.java,v 1.13 2000/06/03 01:52:13 mtsezgin Exp $     
  * $Name:  $   
  * $Locker:  $
  * $Source: /mit/mtsezgin/CVS/Repository/REC/ObjectManager.java,v $
  *  
  **/


import java.awt.Color;
import java.awt.Graphics;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.util.Enumeration;
import java.util.Vector;


/**
  *
  * This class manages the objects recognized by the Tablet.
  * Tablet adds the recognized using the addObject method.
  * The objects are kept in a Vector.
  *
  **/

class      ObjectManager
implements MouseMotionListener,
		   MouseListener,
           KeyListener
{
    ParseSupervisor parse_supervisor;
    SpatialParser   spatial_parser;
	GeometricObject selected_objects[];
    Vector          objects;
    Filter          filter;
	Tablet          tablet;

    /**
    *
    * This is true if the parsed stuff is to be painted.
    * Otherwise the original data points are displayed.
    *
    **/    
    boolean show_parsed_objects = false;


    /**
    *
    * The constructor.
    *
    **/    
    ObjectManager( Tablet tablet )
    {
		this.tablet      = tablet;
        spatial_parser   = new SpatialParser();
        parse_supervisor = new ParseSupervisor( this );
        objects          = new Vector( 10, 0 );
        filter           = new Filter( this );
    }


    /**
    *
    * Adds an object to the objects vector after filtering.
    *
    **/
    public void
    addObject( Object o )
    {
        Vector filtered_output = filter.filter( o );
        for ( int i=0; i<filtered_output.size(); i++ ) {
            addObjectInternal( filtered_output.elementAt(i) );
        }
    }


    /**
    *
    * Removes the argument object from the objects Vector.
    *
    **/
    public boolean
    removeObject( Object object )
    {
        return objects.removeElement( object );
    }


    /**
    *
    * Adds an object to the objects vector.
    *
    **/
    private void
    addObjectInternal( Object o )
    {
        Paintable p = (Paintable)o;
        System.out.println( "addObject " + ((Paintable)o).getType() );
        
        if ( p instanceof Polygon ) {
            if ( Rectangle.isRectangle( (Polygon)o ) ) {
                Rectangle rectangle = ((Polygon)o).getRectangularBounds();
                rectangle.setDataPoints( ((Polygon)o).getDataPoints() );
                addObject( rectangle ); 
                System.out.println( "Converted Polygon to Rectangle" );
                System.out.println( "Direction of the rectangle " + 
                    Direction.toString(
                        (((Polygon)o).getRectangularBounds()
                            ).getMajorAxisDirection()) );
                System.out.println( (((Polygon)o).getRectangularBounds()) );
                            
                return;
            }
            if ( Line.isLine( ((Polygon)o) ) ) {
                addObject( ((Polygon)o).getEdge(0)  ); 
                System.out.println( "Converted Polygon to Line" );
                return;
            }
            if ( Spring.isSpring( ((Polygon)o) ) ) {
                objects.addElement( new Spring( ((Polygon)o) ) );
                return;
            }
        }
        
        if ( tryCombining( p ) ) {
            System.out.println( "Combined two Polygons" );
            return;
        }
        
        normalizeObject( ((Object)p) );
        
        objects.addElement( o );
        System.out.println( "Added " + o );
    }


    /**
    *
    * Look at the statistical information and modify the polygons
    * to look like as they were intended to be.
    *
    **/
    private void
    normalizeObject( Object o )
    {
        System.out.println( "Normalize Object" );
        if ( o instanceof Polygon ) {
            Polygon polygon     = ((Polygon)o);
            double  angle_set[] = polygon.getMajorAngles();
            
            for ( int i=0; i<angle_set.length; i++ ) {
                System.out.println( "Angle " + i + " = " + 
                                     angle_set[i]*180/Math.PI );
            }
            
            angle_set = GeometryUtil.roundAngles( angle_set, Math.PI/18 );

            double tmp_angle_set[] = new double[angle_set.length*3];
            for ( int i=0; i<angle_set.length; i++ ) {
                tmp_angle_set[i                   ] = angle_set[i];
                tmp_angle_set[i+angle_set.length  ] = angle_set[i] + Math.PI;
                tmp_angle_set[i+angle_set.length*2] = angle_set[i] - Math.PI;
            }
            angle_set = tmp_angle_set;
            for ( int i=0; i<angle_set.length; i++ ) {
                System.out.println( "Angle " + i + " = " + 
                                     angle_set[i]*180/Math.PI );
            }
            
            polygon.normalize( angle_set );
        }
    }


    /**
    *
    * Try combining the input object with whatever is present in 
    * the objects vector.
    *
    **/
    private boolean
    tryCombining( Object o )
    {
        Paintable p = (Paintable)o;
        
        if ( ( p instanceof Polygon ) || ( p instanceof Line ) ) {
            for ( int i=0; i<objects.size(); i++ ) {
                if ( objects.elementAt( i ) instanceof Polygon ) {
                    if ( ((Polygon)objects.elementAt(i)).tryCombining(
                        (Object)p, 20) ) {
                        normalizeObject( objects.elementAt(i) );
                        return true;
                    }
                }
            }
        }
        
        return false;
    }
    
    
    /**
    *
    * Draw the objects
    *
    **/
    public void
    paint( Graphics g )
    {
        Paintable object;
        clear( g );
        g.setColor( Color.black );
        System.out.println( "ObjectManager painting" );
        
		for ( Enumeration e = objects.elements(); e.hasMoreElements(); ) {
            object = ((Paintable)e.nextElement());
            object.setGraphicsContext( g );
            if ( show_parsed_objects ) {
                object.paint( g );
            } else {
                object.paintOriginal( g );
            }
        }
		
		if ( selected_objects != null ) {
			g.setColor( Color.green );
			for ( int i=0; i<selected_objects.length; i++ ) {
    	        if ( show_parsed_objects ) {
        	        selected_objects[i].paint( g );
	           	} else {
    	            selected_objects[i].paintOriginal( g );
    	        }
			}
		}
    }
    
    

    /**
    *
    * Clear the screen
    *
    **/    
    public void
    clear( Graphics g )
    {
        System.out.println( "Clearing in ObjectManager" );
        Color old_color = g.getColor();
        g.setColor( Color.white );
        g.fillRect( 0, 0, 1000, 1000 );
        g.setColor( old_color );
    }
    
    
    /**
    *
    * Removes all the objects.
    *
    **/    
    public void
    reset()
    {
        objects.removeAllElements();
    }
    
    
    /**
    *
    * A very primitive undo
    *
    **/    
    public void
    undo()
    {
        objects.remove( objects.size() - 1 );
        paint( Tablet.debug_graphics );
    }
    
    
    /**
    *
    * Returns a vector of all the geometric objects "sufficiently 
	* close to the point passes, determined by the search radius r.
    * For the moment, the only Terminals are searched.
    *
    **/    
    public Vector
    getNeighborObjects( Point p, int r )
    {
        Object          object;
        GeometricObject geometric_object;
        Vector          neighbors = new Vector();
        
        for ( Enumeration e = objects.elements(); e.hasMoreElements(); ) {
            object = e.nextElement();
            if ( object instanceof GeometricObject ) {
                   geometric_object = ((GeometricObject)object);
                if ( geometric_object.pointIsOn( p, r ) ) {
                    neighbors.addElement( geometric_object );
                }
            }
        }
        
        return neighbors;
    }
    
    
    /**
    *
    * Returns a vector of all the geometric objects "sufficiently 
	* close to the point passes, determined by the search radius r.
    * For the moment, the only Terminals are searched. This method
	* looks at the original data points.
    *
    **/    
    public Vector
    getNeighborObjectsOriginal( Point p, int r )
    {
        Object          object;
        GeometricObject geometric_object;
        Vector          neighbors = new Vector();
        
        for ( Enumeration e = objects.elements(); e.hasMoreElements(); ) {
            object = e.nextElement();
            if ( object instanceof GeometricObject ) {
                   geometric_object = ((GeometricObject)object);
                if ( geometric_object.pointIsOnOriginal( p, r ) ) {
                    neighbors.addElement( geometric_object );
                }
            }
        }
        
        return neighbors;
    }
    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseClicked( MouseEvent e ) 
    {
        int             index;
        GeometricObject object;
        
        Point  point     = new Point( e.getPoint() );
        Vector neighbors = getNeighborObjects( point, 10 );
		
        System.out.print( "Clicked on ");
		if ( e.getClickCount() == 2 ) {
			objects.removeAll( neighbors );
			paint( Tablet.debug_graphics );
			return;
		}

        for ( int i=0; i<neighbors.size(); i++ ) {
            object = (GeometricObject)(neighbors.elementAt(i));
            System.out.println( object );
            
            if ( object instanceof Polygon ) {
                Polygon polygon     = ((Polygon)object);
                double  angles[]    = polygon.getAbsolutePositiveAngles();    
                double  window_size = Tablet.window_width;
                Tablet.bar_statistics_module.plotBarGraph( angles, 
                                                           0, 
                                                           Math.PI+window_size, 
                                                           window_size );

                double wrapped_angles[] = new double[angles.length*2];

                int j = 0;
                for ( j = 0;             j<angles.length;         j++ )
                    wrapped_angles[j] = angles[j];
                for ( j = angles.length; j<wrapped_angles.length; j++ )
                    wrapped_angles[j] = angles[j%angles.length] + Math.PI;

                System.out.print( "window_size " + window_size );
                Tablet.sliding_window_statistics_module.plotSlidingWindowGraph( 
                    angles, 
                    0,
                    Math.PI, 
                    Math.PI/180,
                    window_size );
            }
        }
        
        /*
        for ( int i=0; i<neighbors.size(); i++ ) {
            object = (GeometricObject)(neighbors.elementAt(i));
            System.out.println( object );
            
            if ( object instanceof Polygon ) {
                index = ((Polygon)object).closestNeighborIndex( point );
                ((Polygon)object).drawAngleAt( index, point );
            }
        }
        
        for ( int i=0; i<object_manager.objects.size(); i++ ) {
            if (object_manager.objects.elementAt(i) instanceof GeometricObject)
            System.out.println( 
                SpatialRelation.toString( 
                    ((GeometricObject)
                        object_manager.objects.elementAt(i)).spatialRelation( 
                            point )));
        }
        */
    }
    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseEntered( MouseEvent e ) 
    {
    }
    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseExited( MouseEvent e ) 
    {
    }
    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mousePressed( MouseEvent e ) 
    {
        Point  point     = new Point( e.getPoint() );
        Vector neighbors;
		if ( show_parsed_objects ) {
			neighbors = getNeighborObjects( point, 10 );
		} else {
			neighbors = getNeighborObjectsOriginal( point, 10 );
		}
		System.out.println( "Got " + neighbors.size() + " neighbors" );
		GeometricObject objects[] = new GeometricObject[neighbors.size()];
		
		for ( int i=0; i<objects.length; i++ ) {
			objects[i] = (GeometricObject)(neighbors.elementAt( i ));
		}
		if ( objects.length > 0 ) {
			selectObjects( objects );
			paint( Tablet.debug_graphics );
		}
    }
    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseReleased( MouseEvent e ) 
    {
		if ( ( selected_objects != null    ) && 
		     ( selected_objects.length > 0 ) ) {
			tablet.reset();
		}
		deselectObjects();
		paint( Tablet.debug_graphics );		
    }    
    
    /**
    *
    * Handle key stroke events
    *
    **/
    public void
    keyPressed( KeyEvent k )
    {
    }
     
	 
    /**
    *
    * Handle key stroke events
    *
    **/
    public void
    keyReleased( KeyEvent k )
    {    
    }
	
	
    /**
    *
    * Handle key stroke events
    *
    **/
    public void
    keyTyped( KeyEvent k )
    {
        char key = k.getKeyChar();
        
        if ( key == 'p' ) {
            parse();
            return;
        }
    }
    
         
    /**
    *
    * Mouse events
    *
    **/
	public void
	mouseDragged( MouseEvent e )
	{
		
	}
	
	
    /**
    *
    * Mouse events
    *
    **/
	public void
	mouseMoved( MouseEvent e )
	{
	}     
	
	
    /**
    *
    * Deselect the objects 
    *
    **/
    public void
    deselectObjects()
    {
		selected_objects = null;
		tablet.setRecognitionMode( true );
    }
    
         
    /**
    *
    * Select the objects in the argument vector.
    *
    **/
    public void
    selectObjects( GeometricObject[] geometric_objects )
    {
		selected_objects = geometric_objects;
		tablet.setRecognitionMode( false );
    }
    
         
    /**
    *
    * Call the parse method in spatial_parser
    *
    **/
    public void
    parse()
    {
        spatial_parser.parse( this );
        paint( Tablet.debug_graphics );
    }
}


/** 
  * 
  * $Log: ObjectManager.java,v $
  * Revision 1.13  2000/06/03 01:52:13  mtsezgin
  *
  * Removed GeometryUtil.java and delegated the functionality to ObjectManager.
  *
  * Revision 1.12  2000/06/02 21:11:15  mtsezgin
  * *** empty log message ***
  *
  * Revision 1.11  2000/05/26 20:46:44  mtsezgin
  * *** empty log message ***
  *
  * Revision 1.9  2000/05/22 02:42:34  mtsezgin
  *
  * The current version enables polygons to be sketched in pieces.
  *
  * Revision 1.8  2000/05/04 01:36:27  mtsezgin
  *
  * Fixed minor bugs.
  * The current version successfuly parses Motor, Cross, Nail and Ground.
  * In addition the ParseSupervisor is introduced here.
  *
  * Revision 1.7  2000/04/28 04:45:04  mtsezgin
  *
  * Now each GeometricObject keeps the mouse input that was previously
  * discarded. User can switch between seeing the recognized mode and
  * the raw mode. setDataPoints( Polygon points ) and getDataPoints()
  * are added to GeometricObject, and all the implementors are modified
  * accordingly.
  *
  * Revision 1.6  2000/04/25 22:18:09  mtsezgin
  *
  * Fixed some bugs in addObject.
  *
  * Revision 1.5  2000/04/20 03:59:53  mtsezgin
  * *** empty log message ***
  *
  * Revision 1.4  2000/04/13 06:24:08  mtsezgin
  *
  * The current version of the program recognized Crosses, and Shades.
  * Implementors of Terminal and their descendants were modified to
  * implement the changes in GeometricObject.
  *
  * Revision 1.3  2000/04/11 00:41:47  mtsezgin
  *
  * Now the whole package succesfully parses a motor.
  *
  * Revision 1.2  2000/04/06 19:16:23  mtsezgin
  *
  * Modified all the classes to use my Point class which extends java.awt.Point
  * instead of directly using java.awt.Point
  *
  * Revision 1.1.1.1  2000/04/01 03:07:07  mtsezgin
  * Imported sources
  *
  * Revision 1.2  2000/03/31 22:41:04  mtsezgin
  *
  * Started Log tracking.
  *
  *  
  **/
