--[[--
A simple module to module to compare and do arithmetic with time values.

@usage
    local TimeVal = require("ui/timeval")

    local tv_start = TimeVal:now()
    -- Do some stuff.
    -- You can add and subtract `TimeVal` objects.
    local tv_duration = TimeVal:now() - tv_start
    -- If you need more precision (like 2.5 s),
    -- you can add the milliseconds to the seconds.
    local tv_duration_seconds_float = tv_duration.sec + tv_duration.usec/1000000
]]

local dbg = require("dbg")
local util = require("ffi/util")

--[[--
TimeVal object.

@table TimeVal
@int sec floored number of seconds
@int usec remaining number of milliseconds
]]
local TimeVal = {
    sec = 0,
    usec = 0,
}

--[[--
Creates a new TimeVal object.

@usage
    local timev = TimeVal:new{
        sec = 10,
        usec = 10000,
    }

@treturn TimeVal
]]
function TimeVal:new(from_o)
    local o = from_o or {}
    if o.sec == nil then
        o.sec = 0
    end
    if o.usec == nil then
        o.usec = 0
    elseif o.usec > 1000000 then
        o.sec = o.sec + math.floor(o.usec/1000000)
        o.usec = o.usec % 1000000
    end
    setmetatable(o, self)
    self.__index = self
    return o
end


function TimeVal:__lt(time_b)
    if self.sec < time_b.sec then
        return true
    elseif self.sec > time_b.sec then
        return false
    else
        -- self.sec == time_b.sec
        if self.usec < time_b.usec then
            return true
        else
            return false
        end
    end
end

function TimeVal:__le(time_b)
    if self.sec < time_b.sec then
        return true
    elseif self.sec > time_b.sec then
        return false
    else
        -- self.sec == time_b.sec
        if self.usec > time_b.usec then
            return false
        else
            return true
        end
    end
end

function TimeVal:__eq(time_b)
    if self.sec == time_b.sec and self.usec == time_b.usec then
        return true
    else
        return false
    end
end

function TimeVal:__sub(time_b)
    local diff = TimeVal:new{}

    diff.sec = self.sec - time_b.sec
    diff.usec = self.usec - time_b.usec

    if diff.sec < 0 and diff.usec > 0 then
        diff.sec = diff.sec + 1
        diff.usec = diff.usec - 1000000
    elseif diff.sec > 0 and diff.usec < 0 then
        diff.sec = diff.sec - 1
        diff.usec = diff.usec + 1000000
    end

    return diff
end

dbg:guard(TimeVal, '__sub',
          function(self, time_b)
              assert(self.sec > time_b.sec or (self.sec == time_b.sec and self.usec >= time_b.usec),
                     "Subtract the first timeval from the latest, not vice versa.")
          end)

function TimeVal:__add(time_b)
    local sum = TimeVal:new{}

    sum.sec = self.sec + time_b.sec
    sum.usec = self.usec + time_b.usec
    if sum.usec > 1000000 then
        sum.usec = sum.usec - 1000000
        sum.sec = sum.sec + 1
    end

    if sum.sec < 0 and sum.usec > 0 then
        sum.sec = sum.sec + 1
        sum.usec = sum.usec - 1000000
    elseif sum.sec > 0 and sum.usec < 0 then
        sum.sec = sum.sec - 1
        sum.usec = sum.usec + 1000000
    end

    return sum
end

--[[--
Creates a new TimeVal object based on the current time.

@usage
    local TimeVal = require("ui/timeval")
    local tv_start = TimeVal:now()
    -- Do some stuff.
    -- You can add and substract `TimeVal` objects.
    local tv_duration = TimeVal:now() - tv_start

@treturn TimeVal
]]
function TimeVal:now()
    local sec, usec = util.gettime()
    return TimeVal:new{sec = sec, usec = usec}
end

return TimeVal
