local Time = require("modal").Time
local Span = require("modal").Span
local describe = require("busted").describe
local it = require("busted").it
local assert = require("busted").assert

describe("Time", function()
   it("should new with arguments", function()
      local f = Time()
      assert.equal(f.numerator, 0)
      assert.equal(f.denominator, 1)
      f = Time(3, 4)
      assert.equal(f.numerator, 3)
      assert.equal(f.denominator, 4)
      f = Time(6, 8)
      assert.equal(f.numerator, 3)
      assert.equal(f.denominator, 4)
      f = Time(-4, 8)
      assert.equal(f.numerator, -1)
      assert.equal(f.denominator, 2)
      f = Time(4, -8)
      assert.equal(f.numerator, -1)
      assert.equal(f.denominator, 2)
      f = Time(-4, -8)
      assert.equal(f.numerator, 1)
      assert.equal(f.denominator, 2)
      f = Time(1.5)
      assert.equal(f.numerator, 3)
      assert.equal(f.denominator, 2)
      f = Time(-1.5)
      assert.equal(f.numerator, -3)
      assert.equal(f.denominator, 2)
      f = Time(0.777)
      assert.equal(f.numerator, 777)
      assert.equal(f.denominator, 1000)
      f = Time(0.0)
      assert.equal(f.numerator, 0)
      return assert.equal(f.denominator, 1)
   end)
   it("should throw on divide by zero", function()
      return assert.has_error(function()
         return Time(1, 0)
      end)
   end)
   it("should add", function()
      local f1 = Time(1, 2)
      local f2 = Time(1, 2)
      assert.equal(f1 + f2, Time(1))
      f1 = Time(1, 2)
      f2 = Time(1, 3)
      assert.equal(f1 + f2, Time(5, 6))
      f1 = Time(1, 2)
      f2 = Time(-1, 3)
      return assert.equal(f1 + f2, Time(1, 6))
   end)
   it("should subtract", function()
      local f1 = Time(1, 2)
      local f2 = Time(1, 2)
      assert.equal(f1 - f2, Time(0))
      f1 = Time(1, 2)
      f2 = Time(1, 3)
      assert.equal(f1 - f2, Time(1, 6))
      f1 = Time(1, 2)
      f2 = Time(-1, 3)
      return assert.equal(f1 - f2, Time(5, 6))
   end)
   it("should multiply", function()
      local f1 = Time(1, 2)
      local f2 = Time(1, 2)
      assert.equal(f1 * f2, Time(1, 4))
      f1 = Time(1, 2)
      f2 = Time(1, 3)
      assert.equal(f1 * f2, Time(1, 6))
      f1 = Time(1, 2)
      f2 = Time(-1, 3)
      return assert.equal(f1 * f2, Time(-1, 6))
   end)
   it("should divide", function()
      local f1 = Time(1, 2)
      local f2 = Time(1, 2)
      assert.equal(f1 / f2, Time(1))
      f1 = Time(1, 2)
      f2 = Time(1, 3)
      assert.equal(f1 / f2, Time(3, 2))
      f1 = Time(1, 2)
      f2 = Time(-1, 3)
      return assert.equal(f1 / f2, Time(-3, 2))
   end)
   it("should support mod", function()
      local f1 = Time(1, 2)
      local f2 = Time(2, 3)
      assert.equal(f1 % f2, Time(1, 2))
      f1 = Time(3, 4)
      f2 = Time(2, 3)
      return assert.equal(f1 % f2, Time(1, 12))
   end)
   it("should be able to be raised to a power", function()
      local f1 = Time(1, 4)
      local f2 = Time(1, 2)
      assert.equal(f1 ^ f2, 0.5)
      f1 = Time(1, 4)
      f2 = Time(2, 1)
      return assert.equal(f1 ^ f2, Time(1, 16))
   end)
   it("should support negative operator", function()
      local f1 = Time(1, 4)
      return assert.equal(-f1, Time(-1, 4))
   end)
   it("should be able to be floored", function()
      local f1 = Time(1, 4)
      assert.equal(f1:floor(), 0)
      f1 = Time(5, 4)
      assert.equal(f1:floor(), 1)
      f1 = Time(9, 4)
      return assert.equal(f1:floor(), 2)
   end)
   it("should support greater than comparison", function()
      assert.is_true(Time(3, 4) > Time(1, 3))
      assert.is_true(Time(5, 4) > Time(1, 1))
      assert.is_false(Time(1, 3) > Time(1, 2))
      return assert.is_false(Time(5, 4) > Time(7, 4))
   end)
   it("should support less than comparison", function()
      assert.is_true(Time(1, 4) < Time(1, 3))
      assert.is_true(Time(1, 4) < Time(1, 3))
      assert.is_true(Time(5, 4) < Time(7, 3))
      assert.is_false(Time(2, 3) < Time(1, 2))
      return assert.is_false(Time(9, 1) < Time(7, 4))
   end)
   it("should support greater than or equal to comparison", function()
      assert.is_true(Time(3, 4) >= Time(1, 3))
      assert.is_true(Time(1, 3) >= Time(1, 3))
      assert.is_true(Time(-1, 3) >= Time(-7, 3))
      assert.is_true(Time(5, 4) >= Time(5, 4))
      assert.is_false(Time(1, 3) >= Time(1, 2))
      return assert.is_false(Time(5, 4) >= Time(7, 4))
   end)
   it("should support less than or equal to comparison", function()
      assert.is_true(Time(1, 4) <= Time(1, 3))
      assert.is_true(Time(1, 4) <= Time(1, 4))
      assert.is_true(Time(5, 4) <= Time(7, 3))
      assert.is_true(Time(-5, 4) <= Time(7, 3))
      assert.is_false(Time(2, 3) <= Time(1, 2))
      return assert.is_false(Time(9, 1) <= Time(7, 4))
   end)
   it("should support equal to comparison", function()
      assert.is_true(Time(1, 4) == Time(1, 4))
      assert.is_true(Time(5, 4) == Time(10, 8))
      assert.is_true(Time(-2, 3) == Time(8, -12))
      assert.is_true(Time(-1, 3) == Time(-3, 9))
      return assert.is_false(Time(254, 255) == Time(255, 256))
   end)
   it("should support min", function()
      assert.equal(Time(3, 4):min(Time(5, 6)), Time(3, 4))
      assert.equal(Time(3, 4):min(Time(3, 6)), Time(3, 6))
      assert.equal(Time(3, 4):min(Time(-5, 6)), Time(-5, 6))
      return assert.equal(Time(-3, 4):min(Time(-5, 6)), Time(-5, 6))
   end)
   it("should support max", function()
      assert.equal(Time(3, 4):max(Time(5, 6)), Time(5, 6))
      assert.equal(Time(3, 4):max(Time(3, 6)), Time(3, 4))
      assert.equal(Time(3, 4):max(Time(-5, 6)), Time(3, 4))
      return assert.equal(Time(-3, 4):max(Time(-5, 6)), Time(-3, 4))
   end)
   return it("should show string representation", function()
      return assert.equal(Time(1, 2):show(), "1/2")
   end)
end)

describe("wholeCycle", function()
   it("should return the large cycle that contains the span", function()
      local f1 = Time(1, 2)
      local actual = f1:wholeCycle()
      local expected = Span(0, 1)
      assert.same(expected, actual)
      local f2 = Time(3, 2)
      actual = f2:wholeCycle()
      expected = Span(1, 2)
      assert.same(expected, actual)
   end)
end)

describe("cyclePos", function()
   it("should return the position within the cycle as a proper fraction", function()
      local f1 = Time(7, 2)
      local actual = f1:cyclePos()
      local expected = Time(1, 2)
      assert.same(expected, actual)
   end)
end)
