require_relative 'cluster_factory'
require_relative 'point'

module Geometry

=begin rdoc
Circles come in all shapes and sizes, but they're usually round.

== Usage
    circle = Geometry::Circle.new [1,2], 3
    circle = Geometry::Circle.new center:[1,2], radius:3
    circle = Geometry::Circle.new center:[1,2], diameter:6
    circle = Geometry::Circle.new diameter:6
=end

class Circle
	include ClusterFactory

	# @return [Point]   The {Circle}'s center point
	attr_reader :center

	# @return [Number]  The {Circle}'s radius
	attr_reader :radius
	attr_writer :options
	def options
		@options = {} if !@options
		@options
	end
	# @overload new(center, radius)
	#   Construct a {Circle} using a centerpoint and radius
	#   @param [Point]	center  The center point of the {Circle}
	#   @param [Number]	radius	The radius of the {Circle}
	# @overload new(center, radius)
	#   Construct a circle using named center and radius parameters
	#   @option options [Point]	:center (PointZero)
	#   @option options [Number]	:radius
	# @overload new(center, diameter)
	#   Construct a circle using named center and diameter parameters
	#   @option options [Point]	:center (PointZero)
	#   @option options [Number]	:diameter
	def self.new(*args, &block)
	    options, args = args.partition {|a| a.is_a? Hash}
	    options = options.reduce({}, :merge)
	    center, radius = args[0..1]

	    center ||= (options[:center] || PointZero.new)
	    radius ||= options[:radius]

	    if radius
		self.allocate.tap {|circle| circle.send :initialize, center, radius, &block }
	    elsif options.has_key?(:diameter)
		CenterDiameterCircle.new center, options[:diameter], &block
	    else
		raise ArgumentError, "Circle.new requires a radius or a diameter"
	    end
	end

	# Construct a new {Circle} from a centerpoint and radius
	# @param    [Point]	center  The center point of the {Circle}
	# @param    [Number]	radius  The radius of the {Circle}
	# @return   [Circle]	A new {Circle} object
	def initialize(center, radius)
	    @center = Point[center]
	    @radius = radius
	end

	def eql?(other)
	    (self.center == other.center) && (self.radius == other.radius)
	end
	alias :== :eql?

# @!group Accessors
	# @return [Rectangle]	The smallest axis-aligned {Rectangle} that bounds the receiver
	def bounds
	    return Rectangle.new(self.min, self.max)
	end

	# @!attribute [r] diameter
	#   @return [Numeric] The diameter of the {Circle}
	def diameter
	    @radius*2
	end

	# @return [Point]   The upper right corner of the bounding {Rectangle}
	def max
	    @center+radius
	end

	# @return [Point]   The lower left corner of the bounding {Rectangle}
	def min
	    @center-radius
	end

	# @return [Array<Point>]    The lower left and upper right corners of the bounding {Rectangle}
	def minmax
	    [self.min, self.max]
	end
# @!endgroup
    end

    class CenterDiameterCircle < Circle
	# @return [Number]  The {Circle}'s diameter
	attr_reader :diameter

	# Construct a new {Circle} from a centerpoint and a diameter
	# @param    [Point]	center  The center point of the {Circle}
	# @param    [Number]	diameter  The radius of the {Circle}
	# @return   [Circle]	A new {Circle} object
	def initialize(center, diameter)
	    @center = Point[center]
	    @diameter = diameter
	end

	def eql?(other)
	    (self.center == other.center) && (self.diameter == other.diameter)
	end
	alias :== :eql?

# @!group Accessors
	# @return [Number] The {Circle}'s radius
	def radius
	    @diameter/2
	end
# @!endgroup
    end
end