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

describe("new", function()
   it("should create with defaults", function()
      local t = Span()
      assert.are.same(Time(1), t.start)
      assert.are.same(Time(1), t.stop)
   end)
   it("should create with args", function()
      local t = Span(3, 4)
      assert.are.same(Time(3), t.start)
      assert.are.same(Time(4), t.stop)
   end)
   it("should promote numbers to fractions", function()
      local t = Span(0.5, 0.75)
      assert.are.same(Time(1, 2), t.start)
      assert.are.same(Time(3, 4), t.stop)
   end)
end)
describe("spanCycles", function()
   it("should break multi cycle span into pieces", function()
      local t = Span(3 / 4, 7 / 2)
      local spans = t:spanCycles()
      assert.are.same(4, #spans)
      assert.are.same(Time(3, 4), spans[1].start)
      assert.are.same(Time(1, 1), spans[1].stop)
      assert.are.same(Time(1, 1), spans[2].start)
      assert.are.same(Time(2, 1), spans[2].stop)
      assert.are.same(Time(2, 1), spans[3].start)
      assert.are.same(Time(3, 1), spans[3].stop)
      assert.are.same(Time(3, 1), spans[4].start)
      assert.are.same(Time(7, 2), spans[4].stop)
   end)
   it("should preserve subcycle length spans", function()
      local t = Span(1 / 16, 1)
      local spans = t:spanCycles()
      assert.are.same(1, #spans)
      assert.are.same(Time(1, 16), spans[1].start)
      assert.are.same(Time(1, 1), spans[1].stop)
   end)
end)
describe("duration", function()
   it("should return duration of the span", function()
      local t = Span(3 / 4, 7 / 2)
      assert.are.same(Time(11, 4), t:duration())
      t = Span(6 / 7, 10 / 11)
      assert.are.same(Time(4, 77), t:duration())
   end)
end)
describe("midpoint", function()
   it("should return the middle point between span begin and end", function()
      local t = Span(0, 1)
      assert.are.same(Time(1, 2), t:midpoint())
      t = Span(7 / 11, 5 / 4)
      assert.are.same(Time(83, 88), t:midpoint())
   end)
end)
describe("cycleSpan", function()
   it("should return the span as if it started in cycle 0", function()
      local t = Span(5 / 4, 11 / 4)
      assert.are.same(Span(1 / 4, 7 / 4), t:cycleSpan())
   end)
end)
describe("equals", function()
   it("should compare properties", function()
      local t1 = Span(1 / 2, 5 / 4)
      local t2 = Span(1 / 2, 5 / 4)
      assert.are.same(t1, t2)
      t1 = Span(4 / 8, 5 / 4)
      t2 = Span(1 / 2, 10 / 8)
      assert.are.same(t1, t2)
   end)
end)

describe("withTime", function()
   it("should return new span with modified begin time", function()
      local add1
      add1 = function(frac)
         return frac + Time(1)
      end
      local t = Span(1 / 2, 5 / 6)
      local actual = t:withTime(add1)
      local expected = Span(3 / 2, 11 / 6)
      assert.are.same(expected, actual)
   end)
end)

describe("withEnd", function()
   it("should return new span with modified end time", function()
      local add1
      add1 = function(frac)
         return frac + Time(1)
      end
      local t = Span(1 / 2, 5 / 6)
      local actual = t:withEnd(add1)
      local expected = Span(1 / 2, 11 / 6)
      assert.are.same(expected, actual)
   end)
end)

describe("intersection", function()
   it("should return the common timespan between two spans", function()
      local ts1 = Span(1 / 2, 5 / 4)
      local ts2 = Span(2 / 3, 2 / 2)
      assert.are.same(Span(2 / 3, 2 / 2), ts1:sect(ts2))
      assert.are.same(Span(2 / 3, 2 / 2), ts2:sect(ts1))
      ts1 = Span(1 / 2, 5 / 4)
      ts2 = Span(5 / 4, 7 / 4)
      assert.is_nil(ts1:sect(ts2))
      assert.is_nil(ts2:sect(ts1))
   end)
end)

describe("intersection_e", function()
   it("should return the common timespan between two spans", function()
      local ts1 = Span(1 / 2, 5 / 4)
      local ts2 = Span(2 / 3, 2 / 2)
      assert.are.same(Span(2 / 3, 2 / 2), ts1:sect_e(ts2))
      assert.are.same(Span(2 / 3, 2 / 2), ts2:sect_e(ts1))
      ts1 = Span(1 / 2, 5 / 4)
      ts2 = Span(5 / 4, 7 / 4)
      assert.has_error(function()
         return ts1:sect_e(ts2)
      end)
      assert.has_error(function()
         return ts2:sect_e(ts1)
      end)
   end)
end)
