local Rx = require 'Rx'

local Disposable = Rx.Disposable

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

local innerDisposablePrototype = {

    Disposable,
        -- superclass

    _disposable = false,
      -- (Disposable) managed disposable

    _isInnerDisposed = false,
      -- (boolean) whether disposable has been disposed

}

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

function innerDisposablePrototype:__init(disposable)

    self._disposable = disposable
    self._disposable._count = self._disposable._count + 1
    self._isInnerDisposed = false

    return function()

        if not self._disposable._isDisposed and not self._isInnerDisposed then

            self._isInnerDisposed = true
            self._disposable._count = self._disposable._count - 1

            if self._disposable._count == 0 and self._disposable._isPrimaryDisposed then
                self._disposable:Rx_Disposable_dispose()
                self._disposable._underlyingDisposable:dispose()
                self._disposable._underlyingDisposable = nil
            end

        end

    end

end

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

local innerDisposableClass = Rx.Class('Rx.internals.InnerDisposable', innerDisposablePrototype)

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

local refCountDisposablePrototype = {

    Disposable,
        -- superclass

    _underlyingDisposable = false,
      -- (Disposable) managed Disposable

    _isPrimaryDisposed = false,
      -- (boolean) true if :dispose() has been called

    _count = 0,
      -- (number) reference count

}

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

function refCountDisposablePrototype:__init(disposable)

    if not Disposable:isClassOf(disposable) then
        error(string.format("Rx.RefCountDisposable: item (%s) is not a Disposable",
                tostring(disposable)), 2)
    end

    self._underlyingDisposable = disposable
    self._isPrimaryDisposed = false
    self._count = 0

    return function() end

end

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

function refCountDisposablePrototype:dispose()

    if not self._isDisposed and not self._isPrimaryDisposed then

        self._isPrimaryDisposed = true

        if self._count == 0 then

            self:Rx_Disposable_dispose()
            self._underlyingDisposable:dispose()
            self._underlyingDisposable = nil

        end

    end

end

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

function refCountDisposablePrototype:getDisposable()

    return self._isDisposed and Disposable.empty() or innerDisposableClass(self)

end

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

return Rx.Class('Rx.RefCountDisposable', refCountDisposablePrototype)
