# mode: run
# tag: cpp, cpp11, pthread
# cython: experimental_cpp_class_def=True, language_level=2


cdef cypclass Refcounted:
    __dealloc__(self) with gil:
        print("Refcounted destroyed")


def test_recover_name():
    """
    >>> test_recover_name()
    Refcounted destroyed
    0
    """
    r0 = Refcounted()
    if Cy_GETREF(r0) != 2:
        return -1

    cdef Refcounted r1 = recover r0
    if r0 is not NULL:
        return -2
    if Cy_GETREF(r1) != 2:
        return -3

    return 0

def test_recover_iso_name():
    """
    >>> test_recover_iso_name()
    Refcounted destroyed
    0
    """
    cdef iso Refcounted r0 = recover Refcounted()

    cdef Refcounted r1 = recover r0
    if r0 is not NULL:
        return -2
    if Cy_GETREF(r1) != 2:
        return -3

    return 0

def test_recover_and_drop_name():
    """
    >>> test_recover_and_drop_name()
    Refcounted destroyed
    recovered
    0
    """
    r = Refcounted()
    if Cy_GETREF(r) != 2:
        return -1

    recover r
    print("recovered")

    if r is not NULL:
        return -2

    return 0

cdef cypclass DoubleRefcounted(Refcounted):
    DoubleRefcounted get_self(self):
        return self


def test_recover_and_drop_aliased_temporary():
    """
    >>> test_recover_and_drop_aliased_temporary()
    'recover' operand is not isolated
    Refcounted destroyed
    0
    """
    r = DoubleRefcounted()
    if Cy_GETREF(r) != 2:
        return -1

    try:
        recover r.get_self()
        return -2
    except TypeError as e:
        print(e)

    if Cy_GETREF(r) != 2:
        return -3

    return 0

def test_recover_and_drop_cast_aliased_temporary():
    """
    >>> test_recover_and_drop_cast_aliased_temporary()
    'recover' operand is not isolated
    Refcounted destroyed
    0
    """
    r = DoubleRefcounted()
    if Cy_GETREF(r) != 2:
        return -1

    try:
        recover <DoubleRefcounted> r.get_self()
        return -2
    except TypeError as e:
        print(e)

    if Cy_GETREF(r) != 2:
        return -3

    return 0

def test_recover_constructed():
    """
    >>> test_recover_constructed()
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover Refcounted()
    if Cy_GETREF(r) != 2:
        return -1

    return 0

def test_recover_iso_constructed():
    """
    >>> test_recover_iso_constructed()
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover new Refcounted()
    if Cy_GETREF(r) != 2:
        return -1

    return 0

def test_recover_and_drop_constructed():
    """
    >>> test_recover_and_drop_constructed()
    Refcounted destroyed
    recovered
    0
    """
    recover Refcounted()
    print("recovered")

    return 0

cdef cypclass Origin:
    Refcounted field

    __init__(self):
        self.field = Refcounted()

cdef cypclass OriginIso:
    iso Refcounted field

    __init__(self):
        self.field = recover Refcounted()

def test_recover_field():
    """
    >>> test_recover_field()
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover Origin().field
    if Cy_GETREF(r) != 2:
        return -1

    return 0

def test_recover_iso_field():
    """
    >>> test_recover_iso_field()
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover OriginIso().field
    if Cy_GETREF(r) != 2:
        return -1

    return 0

def test_recover_and_drop_field():
    """
    >>> test_recover_and_drop_field()
    Refcounted destroyed
    recovered
    0
    """
    recover Origin().field
    print("recovered")

    return 0

def test_recover_cast_name():
    """
    >>> test_recover_cast_name()
    Refcounted destroyed
    0
    """
    r0 = Refcounted()
    if Cy_GETREF(r0) != 2:
        return -1

    cdef Refcounted r1 = recover <Refcounted> r0
    if r0 is not NULL:
        return -2
    if Cy_GETREF(r1) != 2:
        return -3

    return 0

def test_recover_cast_constructed():
    """
    >>> test_recover_cast_constructed()
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover <Refcounted> Refcounted()
    if Cy_GETREF(r) != 2:
        return -1

    return 0

def test_recover_cast_field():
    """
    >>> test_recover_cast_field()
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover <Refcounted> Origin().field
    if Cy_GETREF(r) != 2:
        return -1

    return 0

cdef cypclass Convertible:
    Refcounted __Refcounted__(self):
        return Refcounted()
    __dealloc__(self) with gil:
        print("Convertible destroyed")

def test_recover_converted_name():
    """
    >>> test_recover_converted_name()
    Convertible destroyed
    Refcounted destroyed
    0
    """
    c = Convertible()
    if Cy_GETREF(c) != 2:
        return -1

    cdef Refcounted r = recover <Refcounted> c
    if c is NULL:
        return -2
    if Cy_GETREF(c) != 2:
        return -3
    if Cy_GETREF(r) != 2:
        return -4

    del c
    return 0

def test_recover_converted_constructed():
    """
    >>> test_recover_converted_constructed()
    Convertible destroyed
    Refcounted destroyed
    0
    """
    cdef Refcounted r = recover <Refcounted> Convertible()
    if Cy_GETREF(r) != 2:
        return -1

    return 0

cdef cypclass OriginConvertible:
    Convertible field

    __init__(self):
        self.field = Convertible()


def test_recover_converted_field():
    """
    >>> test_recover_converted_field()
    Convertible destroyed
    Refcounted destroyed
    0
    """
    o = OriginConvertible()
    if Cy_GETREF(o.field) != 2:
        return -1

    cdef Refcounted r = recover <Refcounted> o.field
    if o.field is NULL:
        return -2
    if Cy_GETREF(o.field) != 2:
        return -3
    if Cy_GETREF(r) != 2:
        return -4

    return 0
