describe "CGPoint" do
  before do
    @point = CGPoint.make(x: 10, y: 20)
  end

  describe ".make" do
    it "should work with options" do
      CGPointEqualToPoint(@point, CGPointMake(10, 20)).should == true
    end

    it "should work with no options" do
      CGPointEqualToPoint(CGPoint.make, CGPointMake(0, 0)).should == true
    end
  end

  describe "#rect_of_size" do
    it "should work" do
      size = CGSizeMake(20, 30)
      @point.rect_of_size(size).should == CGRectMake(10, 20, 20, 30)
    end
  end

  describe "#up" do
    it "should work" do
      point = CGPointMake(1, 1).up(1)
      CGPointEqualToPoint(point, CGPointMake(1, 0)).should == true
    end
  end

  describe "#down" do
    it "should work" do
      point = CGPointMake(1, 1).down(1)
      CGPointEqualToPoint(point, CGPointMake(1, 2)).should == true
    end
  end

  describe "#left" do
    it "should work" do
      point = CGPointMake(1, 1).left(1)
      CGPointEqualToPoint(point, CGPointMake(0, 1)).should == true
    end
  end

  describe "#right" do
    it "should work" do
      point = CGPointMake(1, 1).right(1)
      CGPointEqualToPoint(point, CGPointMake(2, 1)).should == true
    end
  end

  describe "chaining up().down().left().right()" do
    it "should work" do
      point = CGPointMake(1, 1).up(2).down(1).left(2).right(1)
      CGPointEqualToPoint(point, CGPointMake(0, 0)).should == true
    end
  end

  describe "#distance_to" do
    it "should work" do
      point = CGPoint.make(x: 10, y: 100)
      point.distance_to(CGPoint.make(x: 13, y:104)).should == 5
      point.distance_to(CGPoint.make(x: 14, y:103)).should == 5
    end
  end

  describe "#rough_distance_to" do
    it "should work" do
      point = CGPoint.make(x: 10, y: 100)
      point.rough_distance_to(CGPoint.make(x: 13, y:104)).should == 25
      point.rough_distance_to(CGPoint.make(x: 14, y:103)).should == 25
    end
  end

  describe "#distance_within?" do
    it "should work" do
      point = CGPoint.make(x: 10, y: 100)
      point.distance_within?(5, to: CGPoint.make(x: 12, y:104)).should == true
      point.distance_within?(5, to: CGPoint.make(x: 13, y:104)).should == true
      point.distance_within?(5, to: CGPoint.make(x: 14, y:104)).should == false
      point.distance_within?(5, to: CGPoint.make(x: 14, y:102)).should == true
      point.distance_within?(5, to: CGPoint.make(x: 14, y:103)).should == true
      point.distance_within?(5, to: CGPoint.make(x: 14, y:104)).should == false
    end
  end

  describe "#length" do
    it "should work" do
      point = CGPoint.make(x: 3, y: 4)
      point.length.should == 5
    end
  end

  describe "#rough_length" do
    it "should work" do
      point = CGPoint.make(x: 3, y: 4)
      point.rough_length.should == 25
    end
  end

  describe "#angle_to" do
    it "should work" do
      point = CGPoint.make(x: 0, y: 0)
      point.angle_to(CGPoint.make(x: 10, y:0)).should == 0
      (0.785 - point.angle_to(CGPoint.make(x: 10, y:10))).abs.should < 0.01  # ~= Math::PI/4
      (1.57 - point.angle_to(CGPoint.make(x: 0, y:10))).abs.should < 0.01  # ~= Math::PI/2
      (3.14 - point.angle_to(CGPoint.make(x: -10, y:0))).abs.should < 0.01  # ~= Math::PI
      (-1.57 - point.angle_to(CGPoint.make(x: 0, y:-10))).abs.should < 0.01  # ~= -Math::PI/2
    end
  end

  describe "#angle" do
    it "should work with 0 degrees" do
      point = CGPoint.make(x: 3, y: 0)
      point.angle.should == 0
    end
    it "should work with 90 degrees" do
      point = CGPoint.make(x: 0, y: 4)
      point.angle.round(4).should == (Math::PI / 2).round(4)
    end
  end

  describe "#+" do
    it "should work with CGSize" do
      size = CGSizeMake(20, 30)
      (@point + size).should == CGRectMake(10, 20, 20, 30)
    end

    it "should work with CGPoint" do
      point = CGPoint.make(x: 100, y: 200)
      (@point + point).should == CGPointMake(110, 220)
    end

    it "should work with CGRect" do
      point = CGRect.make(x: 100, y: 200, width: 50, height: 50)
      (@point + point).should == CGRectMake(110, 220, 50, 50)
    end
  end

  describe "#*" do
    it "should work with Numeric" do
      point = CGPointMake(12, 24)
      bigger = point * 3
      bigger.x.should == 36
      bigger.y.should == 72
    end
  end

  describe "#/" do
    it "should work with Numeric" do
      point = CGPointMake(12, 24)
      smaller = point / 3
      smaller.x.should == 4
      smaller.y.should == 8
    end
  end

  describe "#- (unary)" do
    it "should work" do
      point = CGPoint.make(x: 100, y: 200)
      (-point).should == CGPoint.new(-100, -200)
    end
  end

  describe "#- (binary)" do
    it "should work" do
      point = CGPoint.make(x: 100, y: 200)
      (point - point).should == CGPoint.new(0, 0)
    end
  end

  describe "#inside?" do
    it "should return true" do
      rect = CGRectMake(0, 0, 100, 100)
      @point.inside?(rect).should == true
    end

    it "should return false" do
      rect = CGRectMake(0, 0, 5, 5)
      @point.inside?(rect).should == false
    end
  end

  describe "#round" do
    it "should work" do
      point = CGPointMake(10.4, 11.5).round
      CGPointEqualToPoint(point, CGPointMake(10, 12)).should == true
    end
  end

  describe "#==?" do
    it "should return true" do
      point = CGPointMake(10, 20)
      @point.should == point
    end

    it "should return false" do
      point = CGPointMake(20, 10)
      @point.should != point
    end
  end

  describe '#to/from_ns_value' do
    it 'should convert to NSValue' do
      val = CGPoint.new(0, 0).to_ns_value
      val.should.be.kind_of(NSValue)
    end
    it 'should convert from NSValue' do
      val = NSValue.valueWithCGPoint(CGPoint.new(0, 0))
      point = CGPoint.from_ns_value(val)
      point.should.be.kind_of(CGPoint)
    end
  end

  describe "#to_ary" do
    it "should allow parallel assigment" do
      x, y = @point
      x.should == 10.0
      y.should == 20.0
    end
  end

end
