local Rx = require 'Rx'

local AnonymousObservable = Rx.AnonymousObservable
local Observer = Rx.Observer

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

return function(self, observerOrOnNext, onError, onCompleted)

    local onNextFunc

    if Observer:isClassOf(observerOrOnNext) then

        if onError ~= nil then
            error("Rx.Observable:tap: onError must be nil if providing an Observer", 2)
        end

        if onCompleted ~= nil then
            error("Rx.Observable:tap: onCompleted must be nil if providing an Observer", 2)
        end

        onNextFunc = function(...)
            return observerOrOnNext:onNext(...)
        end

        onError = function(...)
            return observerOrOnNext:onError(...)
        end

        onCompleted = function(...)
            return observerOrOnNext:onCompleted(...)
        end

    else

        if observerOrOnNext ~= nil and type(observerOrOnNext) ~= 'function' then
            error("Rx.Observable:tap: observerOrOnNext must be an Observer or a function", 2)
        else
            onNextFunc = observerOrOnNext
        end

        if onError ~= nil and type(onError) ~= 'function' then
            error("Rx.Observable:tap: onError must be a function or nil", 2)
        end

        if onCompleted ~= nil and type(onCompleted) ~= 'function' then
            error("Rx.Observable:tap: onCompleted must be a function or nil", 2)
        end

    end

    return AnonymousObservable(

        function(observer)

            local function relayErrorFromAction(success, ...)
                if not success then
                    observer:onError(...)
                end
                return success
            end

            return self:subscribe(

                function(...)
                    if relayErrorFromAction(pcall(function(...)
                        if onNextFunc then
                            onNextFunc(...)
                        end
                    end, ...)) then
                        observer:onNext(...)
                    end
                end,

                function(...)
                    if relayErrorFromAction(pcall(function(...)
                        if onError then
                            onError(...)
                        end
                    end, ...)) then
                        observer:onError(...)
                    end
                end,

                function()
                    if relayErrorFromAction(pcall(function()
                        if onCompleted then
                            onCompleted()
                        end
                    end)) then
                        observer:onCompleted()
                    end
                end)

        end, self)

end
