require_relative 'cluster_factory'
require_relative 'polygon'

module Geometry
=begin rdoc
A {RegularPolygon} is a lot like a {Polygon}, but more regular.

{http://en.wikipedia.org/wiki/Regular_polygon}

== Usage
    polygon = Geometry::RegularPolygon.new sides:4, center:[1,2], radius:3
    polygon = Geometry::RegularPolygon.new sides:6, center:[1,2], diameter:6
=end

    class RegularPolygon < Polygon
	include ClusterFactory

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

	# @return [Number]  The {RegularPolygon}'s number of sides
	attr_reader :edge_count

	# @return [Number]  The {RegularPolygon}'s radius
	attr_reader :radius

	# @overload new(sides, center, radius)
	#   Construct a {RegularPolygon} using a center point and radius
	#   @option options [Number]	:sides	The number of edges
	#   @option options [Point]	:center	(PointZero) The center point of the {RegularPolygon}
	#   @option options [Number]	:radius The radius of the {RegularPolygon}
	# @overload new(sides, center, diameter)
	#   Construct a {RegularPolygon} using a center point and diameter
	#   @option options [Number]	:sides  The number of edges
	#   @option options [Point]	:center	(PointZero) The center point of the {RegularPolygon}
	#   @option options [Number]	:diameter   The diameter of the {RegularPolygon}
	def self.new(options={}, &block)
	    raise ArgumentError, "RegularPolygon requires an edge count" unless options[:sides]

	    center = options[:center]
	    center = center ? Point[center] : Point.zero

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

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

	def eql?(other)
	    (self.center == other.center) && (self.edge_count == other.edge_count) && (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 {RegularPolygon}
	def diameter
	    @radius*2
	end

	# !@attribute [r] edges
	def edges
	    points = self.vertices
	    points.each_cons(2).map {|p1,p2| Edge.new(p1,p2) } + [Edge.new(points.last, points.first)]
	end

	# !@attribute [r] vertices
	#   @return [Array]
	def vertices
	    (0...2*Math::PI).step(2*Math::PI/edge_count).map {|angle| center + Point[Math::cos(angle), Math::sin(angle)]*radius }
	end

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

	# @return [Point]   The lower left corner of the bounding {Rectangle}
	def min
	    @center-Point[radius, 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 DiameterRegularPolygon < RegularPolygon
	# @return [Number]  The {RegularPolygon}'s diameter
	attr_reader :diameter

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

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

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