local Rx = require 'Rx'

local Disposable = Rx.Disposable
local Observer = Rx.Observer

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

local checkedObserverPrototype = {

    Disposable,
    Observer,
        -- superclass

    _observer = false,
        -- (Observer) managed Observer

    _state = false,
        -- (number) state of 0 - idle, 1 - busy, 2 - done

}

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

function checkedObserverPrototype:__init(observer)

    if not Observer:isClassOf(observer) then
        error(string.format('Rx.CheckedObserver: param (%s) is not a valid Observer',
            tostring(observer)), 3)
    end

    self._observer = observer
    self._state = 0

end

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

function checkedObserverPrototype:__inited()

    self._action = function()
        self._state = 2
        self._observer = nil
    end

end

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

function checkedObserverPrototype:onNext(...)

    self:checkAccess()

    local status, err = pcall(self._observer.onNext, self._observer, ...)

    self._state = 0

    if not status then
        error(err)
    end

end

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

function checkedObserverPrototype:onError(...)

    self:checkAccess()

    local status, errorMsg = pcall(self._observer.onError, self._observer, ...)

    self._state = 2

    if not status then
        error(errorMsg)
    end

end

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

function checkedObserverPrototype:onCompleted()

    self:checkAccess()

    local status, errorMsg = pcall(self._observer.onCompleted, self._observer)

    self._state = 2

    if not status then
        error(errorMsg)
    end

end

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

function checkedObserverPrototype:checkAccess()

    if self._state == 1 then
        error("Re-entrancy detected")
    end

    if self._state == 2 then
        error("Observer completed")
    end

    if self._state == 0 then
        self._state = 1
    end

end

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

return Rx.Class('Rx.CheckedObserver', checkedObserverPrototype)
