require 'geometry/arc'
require 'geometry/edge'

module Geometry
=begin
An object representing a set of connected elements, each of which could be an
{Edge} or an {Arc}. Unlike a {Polygon}, a {Path} is not guaranteed to be closed.
=end
    class Path
	attr_reader :elements

	# Construct a new Path from {Point}s, {Edge}s, and {Arc}s
	#  Successive {Point}s will be converted to {Edge}s.
	def initialize(*args)
	    args.map! {|a| (a.is_a?(Array) or a.is_a?(Vector)) ? Point[a] : a}
	    args.each {|a| raise ArgumentError, "Unknown argument type #{a.class}" unless a.is_a?(Point) or a.is_a?(Edge) or a.is_a?(Arc) }

	    @elements = []

	    first = args.shift
	    push first if first.is_a?(Edge) or first.is_a?(Arc)

	    args.reduce(first) do |previous, n|
		case n
		    when Point
			case previous
			    when Point	    then push Edge.new(previous, n)
			    when Arc, Edge  then push Edge.new(previous.last, n) unless previous.last == n
			end
			last
		    when Edge
			case previous
			    when Point	    then push Edge.new(previous, n.first)
			    when Arc, Edge  then push Edge.new(previous.last, n.first) unless previous.last == n.first
			end
			push(n).last
		    when Arc
			case previous
			    when Point
				if previous == n.first
				    raise ArgumentError, "Duplicated point before an Arc"
				else
				    push Edge.new(previous, n.first)
				end
			    when Arc, Edge
				push Edge.new(previous.last, n.first) unless previous.last == n.first
			end
			push(n).last
		    else
			raise ArgumentError, "Unsupported argument type: #{n}"
		end
	    end
	end

	def ==(other)
	    if other.is_a?(Path)
		@elements == other.elements
	    else
		super other
	    end
	end

    # @group Attributes

	# @return [Point]   The upper-right corner of the bounding rectangle that encloses the {Path}
	def max
	    elements.reduce(elements.first.max) {|memo, e| memo.max(e.max) }
	end

	# @return [Point]   The lower-left corner of the bounding rectangle that encloses the {Path}
	def min
	    elements.reduce(elements.first.min) {|memo, e| memo.min(e.max) }
	end

	# @return [Array<Point>]    The lower-left and upper-right corners of the enclosing bounding rectangle
	def minmax
	    elements.reduce(elements.first.minmax) {|memo, e| [memo.first.min(e.min), memo.last.max(e.max)] }
	end

	# @return [Geometry]	The last element in the {Path}
	def last
	    @elements.last
	end

    # @endgroup

	# Append a new geometry element to the {Path}
	# @return [Path]
	def push(arg)
	    @elements.push arg
	    self
	end
    end
end