package jdraw;

import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import jdraw.graphicalobjects.GraphicalObject;

/**
 *
 * A repository for graphical objects.
 *
 */
public class GraphicalObjectManager
{
  /** The squared distance thresold, used to find closest object. */
  private int iDistanceSquareThresold= 16;

  /** The collection containing the graphical objects. */
  private final ArrayList lstObjects= new ArrayList();

  /** The shared listener for bounding box events. */
  private final PropertyChangeListener pplPropertyChangeListener= new PropertyChangeListener()
								      {
									public void propertyChange( final PropertyChangeEvent  ppe )
									{
									  if (ppe.getPropertyName().equals( "BoundingBox" ))
									    fireBoudingBoxChange( ppe );
									}
								      };

  /**
   * Creates a new empty <code>GraphicalObjectManager</code> object.
   */
  public GraphicalObjectManager()
  {
  }

  /**
   * Creates a new <code>GraphicalObjectManager</code> object, with the specified <code>GraphicalObject</code>s.
   */
  public GraphicalObjectManager( final GraphicalObject[]  graphical_objects )
  {
    for ( int i= 0 ; i < graphical_objects.length ; ++i )
      add( graphical_objects[i] );
  }

  /**
   * Adds the specified graphical object to the list of managed objects.
   */
  public void add( final GraphicalObject  grobjObject )
  {
    lstObjects.add( grobjObject );
    grobjObject.addPropertyChangeListener( pplPropertyChangeListener );
  }

  /**
   * Returns the closest object to the specified point, with respect to distance thresold.
   */
  public GraphicalObject getClosestObject( final Point ptPoint )
  {
    int                 iMinDistanceSq= Integer.MAX_VALUE;
    GraphicalObject     gobjResult= null;
    final Iterator      itObjects= iterator();

    while (itObjects.hasNext())
    {
      final GraphicalObject   gobjCurrentObject= (GraphicalObject)itObjects.next();
      final int               iCurrentDistanceSq= gobjCurrentObject.getDistanceSq( ptPoint );

      if ((iCurrentDistanceSq < iDistanceSquareThresold)
	  && (iCurrentDistanceSq < iMinDistanceSq))
      {
	iMinDistanceSq= iCurrentDistanceSq;
	gobjResult= gobjCurrentObject;
      }
    }
    return gobjResult;
  }

  /**
   * Returns the distance thresold used to find closest objects.
   */
  public int getDistanceThresold()
  {
    return (int)Math.sqrt( iDistanceSquareThresold );
  }

  /**
   * Returns an iterator over the graphical objects managed.
   */
  public Iterator iterator()
  {
    return lstObjects.iterator();
  }

  /**
   * Removes the specified graphical object to the list of managed objects.
   *
   * @result  <code>true</code> if the specified object where present and removed.
   */
  public boolean remove( final GraphicalObject grobjObject )
  {
    return lstObjects.remove( grobjObject );
  }

  /**
   * Sets the distance thresold used to find closest objects.
   */
  public void setDistanceThresold( final int iNewDistanceThresold )
  {
    iDistanceSquareThresold= (int)Math.pow( iNewDistanceThresold, 2 );
  }


  // ****************************** Listeners management ******************************

  private final ArrayList lstPropertyChangeListeners= new ArrayList();

  private java.awt.Rectangle rectInitialBoundingBox;

  /**
   * Adds a BoudingBoxListener to this graphical object.
   */
  public void addPropertyChangeListener( final PropertyChangeListener  pplListener )
  {
    lstPropertyChangeListeners.add( pplListener );
  }

  /**
   * Removes a BoudingBoxListener from this graphical object.
   */
  public void removePropertyChangeListener( final PropertyChangeListener  pplListener )
  {
    lstPropertyChangeListeners.remove( pplListener );
  }

  /**
   * Fires the specified PropertyChangedEvent.
   */
  protected void fireBoudingBoxChange( final PropertyChangeEvent  ppe )
  {
    final Iterator      iterator= lstPropertyChangeListeners.iterator();

    while (iterator.hasNext())
      ((PropertyChangeListener)iterator.next()).propertyChange( ppe );
  }
}

