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

package rec.house;

import java.awt.*;
import java.util.*;
import rec.util.*;
import rec.geo.*;
import rec.core.*;
import rec.sys.*;

/**
 * WPolygon is the base Polygon call and must be overridden.
 */
public class Region extends WPolygon implements Namable
{
  String name;
  
  /**
   * The bordering walls.
   */
  HashSet walls;  

  static {
    Colors.setDefaultColor( "region", "stroke" );
  }

  protected Region( GPoint[] points )
  {
    super( points );
    walls = new HashSet();
    shrink();
  }

  protected Region( Vector points )
  {
    super( points );
    walls = new HashSet();
    shrink();
  }

  protected Region( PolygonInfo pi )
  {
    super( pi );
    walls = new HashSet();
    shrink();
  }



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

  /**
   * This blank constructor should be defined in all subclasses.
   */
  protected Region()
  {
    // For subclass use.
    walls = new HashSet();
  }


  /**
   * This copies all the regions traits from the alledget mama region
   * r
   */
  public void makeLike( Region r )
  {
    // copy over the name
    this.name = r.name;
  }

  private void shrink() 
  {
    GPoint cent = getLocation().getCenter();

    //    for ( int i = 0; i < m_
  }

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

  public void draw( Graphics g )
  {
    Polygon pg = new Polygon();

    GPoint[] pts = m_Polygon.getPoints();

    GPoint cent = getLocation().getCenter();
    for ( int i = 0; i < pts.length; i++ )
      {
	int x = (int)( pts[i].x + (cent.x - pts[i].x) * 0.05);
	int y = (int)( pts[i].y + (cent.y - pts[i].y) * 0.05);
	pg.addPoint( x, y );
      }

    Color c = getColor();
    Color bk = g.getColor();

    if ( c == null )
      c = Colors.getColor( "region" );

    c = Colors.fade( c );

    g.setColor( new Color( c.getRed(), c.getGreen(), c.getBlue(), 120 ) );
    g.fillPolygon( pg );

    g.setColor( Colors.getColor( "text" ) );
    Font f = g.getFont();
    f = f.deriveFont( Font.BOLD );
    g.setFont( f );
    
    FontMetrics fms = g.getFontMetrics();

    String nnn = null;
    if ( name != null )
      nnn = name;
    else
      //      g.drawString( "area=" + ((int)m_Polygon.area()),
      //		  (int)cent.x, (int)cent.y );
      nnn = "" + getId();
    
    int ddx = fms.stringWidth( nnn );

    g.drawString( nnn,
		  (int)cent.x - ddx / 2, (int)cent.y );

    g.setColor( bk );
    //    super.draw( g );

    // make sure walls drawn over us
    Iterator it = walls.iterator();
    //    Console.writeln( "Drawing wall" );
    //    Console.indent();
    while( it.hasNext() )
      {
	//	Console.writeln( "We draw wall" );
	( (Wall)it.next() ).draw( g, true );
      }
    //    Console.outdent();

  }


  public double area( )
  {
    return m_Polygon.area();
  }

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


  public Iterator walls( )
  {
    return ((HashSet)walls.clone()).iterator();
  }

  public void addWall( Wall wall )
  {
    walls.add( wall );
    //    surface().register( this, wall );

    final Wall w = wall;
    Transition t = new FlipTransition( getSurface() ) {
      public void doUndo()
	{
	  removeWall( w );
	}

      public void doRedo()
	{
	  addWall( w );
	}

      public String name()
	{
	  return "addWall";
	}
    };

    recordChange( t );
  }

  public void removeWall( Wall wall )
  {
    walls.remove( wall );
    //surface().unregister( this, wall );

    final Wall w = wall;

    Transition t = new FlipTransition( getSurface() ) {
      public void doUndo()
	{
	  addWall( w );
	}

      public void doRedo()
	{
	  removeWall( w );
	}

      public String name()
	{
	  return "remWall";
	}

    };

    recordChange( t );
  }


  public Object clone()
  {
    Region ret = new Region();

    ret.walls = (HashSet)walls.clone();

    ret.m_Polygon = new GPolygon( m_Polygon );

    return( ret );
  }


  public boolean contains( WLine l, double offset )
  {
    return m_Polygon.contains( l.getHead(), offset ) 
      && m_Polygon.contains( l.getTail(), offset );
  }


  public boolean contains( Region r, double slack )
  {
    return m_Polygon.contains( r.m_Polygon, slack );
  }

  /**
   * Return region which is outside passed region.  Return null if
   * none.  
   *
   * Behaviour undefined if there are 2 outside regions formed by
   * cutting out 'little'.
   */
  public Region getRemainder( Region little, double slack )
  {
    Vector v = m_Polygon.getRemainder( little.m_Polygon, slack );

    if ( v.size() < 3 )
      {
	Console.writeln( "Remainder too small (" + v.size() + " points)" );
	return null;
      }

    GPoint[] pt = new GPoint[ v.size() ];
    for ( int i = 0; i < pt.length; i++ )
      {
	pt[i] = (GPoint)v.elementAt( i );
	Console.writeln( "remainder point=" + pt[i] );
      }

    return new Region( pt );
  }


  public boolean overlaps( Region r )
  {
    return m_Polygon.overlaps( r.m_Polygon );
  }

  /**
   * @return 2 regions formed by line, or null.
   */
  public HashSet splitOn( WLine cut_line )
  {
    GSegment l = cut_line.getLine();
    GPoint[] pts = m_Polygon.getPoints();
    GSegment[] sides = m_Polygon.getSides();
    Vector v1 = new Vector(), v2 = new Vector();
    boolean side1 = true;

    double d = 100000;
    int best = 0;

    // this is to deal with concave regions.  Needs better solution.
    if ( !m_Polygon.contains( cut_line.getMidpoint(),
			      RegionRecognizer.BREAK ) )
      {
	Console.writeln( "cut line not in polygon." );
	return null;
      }

    // divide points into 2 polys by looping around and switching polygons
    // when we find intersections with the cutting line l
    for ( int i = 0; i < pts.length; i++ )
      {
	System.err.println( "* look at " + pts[i] );
	if ( side1 )
	  v1.addElement( pts[i] );
	else
	  v2.addElement( pts[i] );

	// get distance from point to i-th segment.
	GPoint p = sides[i].getIntersection( l );

	if ( (p != null) && sides[i].includes( p ) )
	  {
	    System.err.println( "* Swap sides" );

	    // If point is _very close_ to the old vertex, then do not
	    // add to the Vector (to avoid the split along edge and
	    // double up to have collapsed rectanglue regions bug)
	    if (! p.includes( pts[i], 1 ) 
		&& !p.includes( pts[(i+1)%pts.length], 1 ) )
	      {
		System.err.println( "* cut point: " + p );
		v1.addElement( p );
		v2.addElement( p );
	      }
	    side1 = !side1;
	  }
      }

    Console.writeln( "Point division: " + v1 + " and " + v2 );

    // check for empty or 1d polys
    if ( ( v1.size() < 3 ) || ( v2.size() < 3 ) )
      return null;

    // make the new polygons
    HashSet s = new HashSet();
    s.add( new Region( v1 ) );
    s.add( new Region( v2 ) );

    return s;
  }

  
  public String getName()
  {
    return name;
  }

  public void setName( String name )
  {
    this.name = name.toString();
  }


}
