local Rx = require 'Rx'

local AnonymousObservable = Rx.AnonymousObservable
local Observable = Rx.Observable
local Scheduler = Rx.Scheduler
local SchedulerArgument = Rx.internals.SchedulerArgument

--------------------------------------------------------------------------------

local function observableTimerTimeSpan(afterTime, scheduler)

    return AnonymousObservable(function(observer)

        return scheduler:scheduleFuture(Scheduler.normalize(afterTime),
            function()
                observer:onNext(0)
                observer:onCompleted()
            end)

    end)

end

--------------------------------------------------------------------------------

local function observableTimerDateAndPeriod(afterTime, period, scheduler)

    return AnonymousObservable(function(observer)

        local d = afterTime
        local p = Scheduler.normalize(period)

        return scheduler:scheduleRecursiveFuture(1, d - scheduler:now(),
            function(count, recurseFn)

                local now = scheduler:now()

                if p > 0 then
                    d = d + p
                    if d <= now then
                        d = now + p
                    end
                end

                observer:onNext(count)

                recurseFn(count + 1, d - now)

            end)

    end)

end

--------------------------------------------------------------------------------

local function observableTimerTimeSpanAndPeriod(afterTime, period, scheduler)

    if afterTime == period then

        return AnonymousObservable(function(observer)

            return scheduler:schedulePeriodic(1, period,
                function(count)
                    observer:onNext(count)
                    return count + 1
                end)

        end)

    else

        return Observable.defer(function()
            return observableTimerDateAndPeriod(scheduler:now() + afterTime, period, scheduler)
        end)

    end

end

--------------------------------------------------------------------------------

return function(afterTime, periodOrScheduler, scheduler)

    local period

    if Scheduler:isClassOf(periodOrScheduler) then
        if scheduler ~= nil then
            error("Rx.Observable.timer: period argument must be nil or a number", 2)
        else
            scheduler = periodOrScheduler
            period = nil
        end
    else
        period = periodOrScheduler
    end

    scheduler = SchedulerArgument.validateWithoutDefault('Rx.Observable.timer', scheduler)

    if type(afterTime) ~= 'number' then
        error("Rx.Observable.timer: afterTime argument must be a number", 2)
    end

    if period ~= nil and type(period) ~= 'number' then
        error("Rx.Observable.timer: period argument must be nil or a number", 2)
    end

    if not period then
        return observableTimerTimeSpan(afterTime, scheduler)
    else
        return observableTimerTimeSpanAndPeriod(afterTime, period, scheduler)
    end

end
