// (c) MIT 2003.  All rights reserved.

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	GeometryCalculator.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	06-XX-98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Drawing recognition system
//
// HISTORY:     11-15-98 archon
//              Now aware of new rec.geo package
//
////////////////////////////////////////////////////////////////////////////////

package rec.sys;

import java.util.*;
import java.awt.*;
import rec.util.*;
import rec.geo.*;
import rec.loc.*;

public class GeometryCalculator
{
  private Surface m_Surface;
  
  public GeometryCalculator( Surface s )
  {
    m_Surface = s;
  }

  //////////////////////////////////////////////////////////////////////////////

  public Collection getVisibleWidgets()
  {
    return m_Surface.getVisibleWidgets();
  }

  public Collection getSurfaceWidgets()
  {
    return m_Surface.getSurfaceWidgets();
  }
  

  public Iterator findInRegion( Region r )
  {
    return( r.filter( getVisibleWidgets().iterator() ) );
  }

  public VisibleWidget findClosestWidgets( GPoint gp, double distance )
  {
    Assert.check( distance >= 0.0, "distance cannot be negative" );

    Iterator e = getVisibleWidgets().iterator();

    if( e == null )
      return( null );

    VisibleWidget ret = null;
    double sd = -1;

    while( e.hasNext() ) {

      VisibleWidget vw = (VisibleWidget)e.next();

      double d = Geometry.distance( gp, vw.getLocation().getCenter() );

      if( d <= distance ) {

	if( ret == null ) {
	  ret = vw;
	  sd = d;
	}
	else if( d < sd ) {
	  ret = vw;
	  sd = d;
	}
      }
    }

    return( ret );
  }

  public VisibleWidget findClosestWithCheck( GPoint gp, GeoFunction gf )
  {
    Iterator e = getVisibleWidgets().iterator();

    if( e == null )
      return( null );

    VisibleWidget ret = null;
    double sd = -1;

    while( e.hasNext() ) {

      VisibleWidget vw = (VisibleWidget)e.next();

      // if no pass check, then bail on this one.
      if ( !gf.check( vw ) )
	continue;

      double d = Geometry.distance( gp, vw.getLocation().getCenter() );

	if( ret == null ) {
	  ret = vw;
	  sd = d;
	}
	else if( d < sd ) {
	  ret = vw;
	  sd = d;
	}
    }

    return( ret );
  }

  public VisibleWidget findClosestSurfaceWidgetWithCheck( GPoint gp,
							  GeoFunction gf )
  {
    Iterator e = getSurfaceWidgets().iterator();

    if( e == null )
      return( null );

    VisibleWidget ret = null;
    double sd = -1;

    while( e.hasNext() ) {

      VisibleWidget vw = (VisibleWidget)e.next();

      // if no pass check, then bail on this one.
      if ( !gf.check( vw ) )
	continue;

      double d = Geometry.distance( gp, vw.getLocation().getCenter() );

	if( ret == null ) {
	  ret = vw;
	  sd = d;
	}
	else if( d < sd ) {
	  ret = vw;
	  sd = d;
	}
    }

    return( ret );
  }


  public VisibleWidget findSelectedWidgets( GPoint gp )
  {
    final GPoint pf = gp;

    VisibleWidget ret = findWidget( new GeoFunction() {
      public boolean check( VisibleWidget vw ) {
	return( (vw instanceof VisibleWidget) && vw.includes( pf, 5.0 ) );
      }
    } );
    Assert.check( (ret == null) || (ret instanceof VisibleWidget), 
		  ret + " is not a visible widget" ); 
    return (VisibleWidget)ret;
  }

  public VisibleWidget findSelectedOnSurface( GPoint gp )
  {
    final GPoint pf = gp;

    VisibleWidget ret = findOnSurface( new GeoFunction() {
      public boolean check( VisibleWidget vw ) {
	return( vw.includes( pf, 5.0 ) );
      }
    } );
    return ret;
  }  

  public VisibleWidget findSelectedWithCheck( GPoint gp, GeoFunction gf )
  {
    final GPoint pf = gp;

    GeoFunction gg = new GeoFunction() {
      public boolean check( VisibleWidget vw ) {
	return( (vw instanceof VisibleWidget) && vw.includes( pf, 5.0 ) );
      }
    };

    VisibleWidget ret =  findWidget( new SetFilter( gf, gg ) );

    Assert.check( (ret == null) || (ret instanceof VisibleWidget), 
		  ret + " is not a visible widget" ); 

    return (VisibleWidget)ret;

  }

  public VisibleWidget findWidget( GeoFunction f )
  {
    Collection c = getVisibleWidgets();

    return findWidgetInCollection( f, c );
  }

  protected VisibleWidget findWidgetInCollection( GeoFunction f,
						  Collection c )
  {
    Iterator e = c.iterator();

    if( e == null )
      return( null );

    while( e.hasNext() ) {

      VisibleWidget vw = (VisibleWidget)e.next();

      if( f.check( vw ) )
	return( vw );
    }

    return( null );
    
  }
  
  public VisibleWidget findOnSurface( GeoFunction f )
  {
    Collection c = m_Surface.getSurfacePool().getWidgets();

    return findWidgetInCollection( f, c );
  }

  /**
   * reschedule all widgets which pass the test for rerecognition.
   * @return the number of widgets found.
   */
  public int recheck( GeoFunction f )
  {
    int found = 0;
    
    Iterator e = getVisibleWidgets().iterator();

    if( e == null )
      return 0;

    while( e.hasNext() ) {

      VisibleWidget vw = (VisibleWidget)e.next();

      if( f.check( vw ) )
	{
	  m_Surface.rerecognizeWidget( vw );
	  found++;
	}
    }

    return found;
  }


}

/////////////////////////////////////////////////////////////////////////////
