"""
Node with self-reference, __repr__, and __str__
"""


class LListNode:
    """Node to be used in linked list"""
    
    def __init__(self: 'LListNode', value: object, 
                 nxt: 'LListNode' =None) -> None:
        """Create a new LListNode containing value and with next node nxt

        nxt --- None if and only if we are on the last node
        value --- always a Python object, there are no empty nodes        
        """
        self.value, self.nxt = value, nxt
        
    def __repr__(self: 'LListNode') -> str:
        """String representation of this LListNode that nests LListNodes"""
        if self.nxt is None:
            return 'LListNode({})'.format(repr(self.value))
        else:
            return 'LListNode({}, {})'.format(repr(self.value), repr(self.nxt))

    def __str__(self: 'LListNode') -> str:
        """String representation of this LListNode that shows it as one link
        in a chain"""
        return '{} -> {}'.format(str(self.value), str(self.nxt))


class LinkedList:
    """Collection of LListNodes"""

    def __init__(self: 'LinkedList') -> None:
        """Create an empty LinkedList"""
        self.front = None
        self.size = 0

    def insert(self: 'LinkedList', value: object) -> None:
        """Insert LListNode with value at front of self
        
        >>> lnk = LinkedList()
        >>> lnk.insert(0)
        >>> lnk.insert(1)
        >>> lnk.insert(2)
        >>> str(lnk.front)
        '2 -> 1 -> 0 -> None'
        >>> lnk.size
        3
        """
        self.front = LListNode(value, self.front)
        self.size += 1

    def delete_front(self: 'LinkedList') -> None:
        """Delete front LListNode from self

        self.front must not be None

        >>> lnk = LinkedList()
        >>> lnk.insert(0)
        >>> lnk.insert(1)
        >>> lnk.insert(2)
        >>> lnk.delete_front()
        >>> str(lnk.front)
        '1 -> 0 -> None'
        >>> lnk.size
        2
        """
        self.front = self.front.nxt
        self.size -= 1

def reverse(ln: LListNode) -> LListNode:
    """Return the linked list starting 
    at ln in reverse order

    >>> ln = LListNode(0)
    >>> ln1 = LListNode(1, ln)
    >>> ln2 = LListNode(2, ln1)
    >>> ln3 = LListNode(3, ln2)
    >>> lnr = reverse(ln3)
    >>> str(lnr)
    '0 -> 1 -> 2 -> 3 -> None'
    """
    if ln.nxt:
        last = ln
        new_node = reverse(last.nxt)
        last.nxt.nxt, last.nxt = last, None
        return new_node
    else:
        return ln

if __name__ == '__main__':
    import doctest
    doctest.testmod()
