/*******************************************************************************
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 * 
 * Contact Info:
 * 	Bruce Donald
 * 	Duke University
 * 	Department of Computer Science
 * 	Levine Science Research Center (LSRC)
 * 	Durham
 * 	NC 27708-0129 
 * 	USA
 * 	brd@cs.duke.edu
 * 
 * Copyright (C) 2011 Jeffrey W. Martin and Bruce R. Donald
 * 
 * <signature of Bruce Donald>, April 2011
 * Bruce Donald, Professor of Computer Science
 ******************************************************************************/


package edu.duke.donaldLab.jdshot.disco.cgal;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import edu.duke.donaldLab.share.geom.Vector2;
import edu.duke.donaldLab.share.io.HashCalculator;

public class Circle extends AbstractCleanable implements Serializable
{
	/**************************
	 *   Definitions
	 **************************/
	
	private static final long serialVersionUID = -8178661231591223781L;
	
	public static enum Orientation
	{
		Clockwise,
		Counterclockwise;
	}
	
	public static enum Side
	{
		BoundedSide,
		UnboundedSide,
		Boundary;
	}
	
	private static final Orientation DefaultOrientation = Orientation.Clockwise;
	
	
	/**************************
	 *   Data Members
	 **************************/
	
	private static Cleaner m_cleaner;
	
	
	/**************************
	 *   Constructors
	 **************************/
	
	static
	{
		m_cleaner = new Cleaner( )
		{
			@Override
			public void cleanup( int id )
			{
				Circle.cleanup( id );
			}
		};
	}
	
	public Circle( Vector2 center, double squaredRadius )
	{
		this( center, squaredRadius, DefaultOrientation );
	}
	
	public Circle( Vector2 center, double squaredRadius, Orientation orientation )
	{
		super( m_cleaner );
		ShotCgal.addReference( this, m_cleaner );
		init( center.x, center.y, squaredRadius, orientation.ordinal() );
	}
	
	private Circle( int id, long pointer )
	{
		// used by native methods
		super( id, pointer, m_cleaner );
		ShotCgal.addReference( this, m_cleaner );
	}
	
	
	/**************************
	 *   Static Methods
	 **************************/
	
	public static native void cleanup( int id );
	
	
	/**************************
	 *   Methods
	 **************************/
	
	public Orientation getOrientation( )
	{
		return Orientation.values()[nativeGetOrientation()];
	}
	
	public Side getSide( Vertex vertex )
	{
		return Side.values()[nativeGetSide( vertex )];
	}
	
	public Side getSide( Vector2 point )
	{
		double distSq = point.getSquaredDistance( getCenter() );
		double radiusSq = getSquaredRadius();
		
		if( radiusSq == distSq )
		{
			return Side.Boundary;
		}
		else if( distSq > radiusSq )
		{
			return Side.UnboundedSide;
		}
		else
		{
			return Side.BoundedSide;
		}
	}
	
	public edu.duke.donaldLab.share.geom.Circle toCircle( )
	{
		return new edu.duke.donaldLab.share.geom.Circle( getCenter(), Math.sqrt( getSquaredRadius() ) );
	}
	
	@Override
	public int hashCode( )
	{
		Vector2 point = getCenter();
		return HashCalculator.combineHashes(
			new Double( point.x ).hashCode(),
			new Double( point.y ).hashCode(),
			new Double( getSquaredRadius() ).hashCode()
		);
	}
	
	@Override
	public boolean equals( Object other )
	{
		if( other == null )
		{
			return false;
		}
		else if( !( other instanceof Circle ) )
		{
			return false;
		}
		
		return equals( (Circle)other );
	}
	
	public native boolean isOnBoundedSide( Face face );
	public native boolean isOnUnboundedSide( Face face );
	public native boolean isOnBoundedSide( Vertex vertex );
	public native boolean isOnUnboundedSide( Vertex vertex );
	
	public native boolean equals( Circle other );
	public native Vector2 getCenter( );
	public native double getSquaredRadius( );
	public native int nativeGetOrientation( );
	public native int nativeGetSide( Vertex vertex );
	
	
	/**************************
	 *   Functions
	 **************************/
	
	private native void init( double x, double y, double squaredRadius, int orientationId );

	private void writeObject( ObjectOutputStream out )
	throws IOException
	{
		Vector2 point = getCenter();
		out.writeDouble( point.x );
		out.writeDouble( point.y );
		out.writeDouble( getSquaredRadius() );
		out.writeObject( getOrientation() );
	}
	
	private void readObject( ObjectInputStream in )
	throws IOException, ClassNotFoundException
	{
		setCleaner( m_cleaner );
		ShotCgal.addReference( this, m_cleaner );
		init(
			in.readDouble(),
			in.readDouble(),
			in.readDouble(),
			((Orientation)in.readObject()).ordinal()
		);
	}
}
