require 'minitest/autorun'
require 'geometry/point_zero'

describe Geometry::PointZero do
    let(:zero) { Geometry::PointZero.new }

    describe "arithmetic" do
	let(:left) { Point[1,2] }
	let(:right) { Point[3,4] }
	
	it "must have +@" do
	    (+zero).must_be :eql?, 0
	    (+zero).must_be_instance_of(Geometry::PointZero)
	end
	
	it "must have unary negation" do
	    (-zero).must_be :eql?, 0
	    (-zero).must_be_instance_of(Geometry::PointZero)
	end

	describe "Accessors" do
	    it "must return 0 for array access" do
		zero[3].must_equal 0
	    end

	    it "must return 0 for named element access" do
		zero.x.must_equal 0
		zero.y.must_equal 0
		zero.z.must_equal 0
	    end
	end

	describe "when adding" do
	    it "must return a number" do
		(zero + 3).must_equal 3
		(3 + zero).must_equal 3
	    end

	    it "return a Point when adding two Points" do
		(zero + right).must_be_kind_of Point
		(left + zero).must_be_kind_of Point
	    end
	    
	    it "must return an Array when adding an array" do
		(zero + [5,6]).must_equal [5,6]
#		([5,6] + zero).must_equal [5,6]
	    end

	    it "must return a Point when adding a Size" do
		(zero + Size[5,6]).must_be_instance_of(Point)
		(zero + Size[5,6]).must_equal Point[5,6]
	    end
	end
	
	describe "when subtracting" do
	    it "must return a number" do
		(zero - 3).must_equal -3
		(3 - zero).must_equal 3
	    end

	    it "return a Point when subtracting two Points" do
		(zero - right).must_equal Point[-3,-4]
		(left - zero).must_equal Point[1,2]
	    end
	    
	    it "must return a Point when subtracting an array" do
		(zero - [5,6]).must_equal [-5, -6]
#		([5,6] - zero).must_equal [5,6]
	    end

	    it "must return a Point when subtracting a Size" do
		(zero - Size[5,6]).must_be_instance_of(Point)
		(zero - Size[5,6]).must_equal Point[-5,-6]
	    end
	end
	
	describe "multiplication" do
	    it "must return 0 for scalars" do
		(zero * 3).must_equal 0
		(zero * 3.0).must_equal 0.0
	    end

	    it "must return 0 for Points" do
		(zero * Point[1,2]).must_equal 0
	    end

	    it "must return 0 for Vectors" do
		(zero * Vector[2,3]).must_equal 0
	    end
	end

	describe "division" do
	    it "must return 0 for non-zero scalars" do
		(zero / 3).must_equal 0
		(zero / 4.0).must_equal 0
	    end

	    it "must raise an exception when divided by 0" do
		lambda { zero / 0 }.must_raise ZeroDivisionError
	    end

	    it "must raise an exception for Points" do
		lambda { zero / Point[1,2] }.must_raise Geometry::OperationNotDefined
	    end

	    it "must raise an exception for Vectors" do
		lambda { zero / Vector[1,2] }.must_raise Geometry::OperationNotDefined
	    end

	end

    end

    describe "coercion" do
	it "must coerce Arrays into Points" do
	    zero.coerce([3,4]).must_equal [Point[3,4], Point[0,0]]
	end
	
	it "must coerce Vectors into Vectors" do
	    zero.coerce(Vector[3,4]).must_equal [Vector[3,4], Vector[0,0]]
	end

	it "must coerce Points into Points" do
	    zero.coerce(Point[5,6]).must_equal [Point[5,6], Point[0,0]]
	end
    end

    describe "comparison" do
	subject { Geometry::PointZero.new }
	
	it "must be equal to 0 and 0.0" do
	    zero.must_be :eql?, 0
	    zero.must_be :eql?, 0.0
	end

	it "must not be equal to a non-zero number" do
	    1.wont_equal zero
	    3.14.wont_equal zero
	end

	it "must be equal to an Array of zeros" do
	    zero.must_be :==, [0,0]
	    zero.must_be :eql?, [0,0]
	    zero.must_be :===, [0,0]
	    [0,0].must_equal zero
	    subject.must_equal [0,0]
	end
	
	it "must not be equal to a non-zero Array" do
	    zero.wont_equal [3,2]
	    [3,2].wont_equal zero
	end
	
	it "must be equal to a Point at the origin" do
	    zero.must_be :==, Point[0,0]
	    zero.must_be :eql?, Point[0,0]
	    zero.must_be :===, Point[0,0]
	    Point[0,0].must_equal zero
	    subject.must_equal Point[0,0]
	end
	
	it "must not be equal to a Point not at the origin" do
	    zero.wont_equal Point[3,2]
	    Point[3,2].wont_equal zero
	end
	
	it "must be equal to an Vector of zeroes" do
	    zero.must_be :eql?, Vector[0,0]
	    Vector[0,0].must_equal zero
	end
	
	it "must not be equal to a non-zero Vector" do
	    zero.wont_equal Vector[3,2]
	    Vector[3,2].wont_equal zero
	end
    end
end