# This is the node definition. Allowing each 'chain link' to hold data and point to the next
class DerekLinkedListNode:
    def __init__(self, key, item) -> None:
        self.key = key
        self.data = item
        self.next: DerekLinkedListNode | None = None

# This is the real meat of the linked list
class DerekLinkedList:
    # On init point to the first node and hold the total length
    def __init__(self) -> None:
        self.head: DerekLinkedListNode | None = None
        self.len: int = 0

    def __iter__(self):
        current = self.head                             # Set the head of the linked list
        while current:                                  # if the value exists
            yield current.data                          # return the data
            current = current.next                      # set the next link

    # Appends an item to the end of the list
    def append(self, key, item) -> None:
        self.len += 1                                   # update the overall length
        newNode = DerekLinkedListNode(key, item)        # create the node to insert
        if not self.head:                               # if the head doesnt exist
            self.head = newNode                             # set the head as the node and return
            return
        cur = self.head                                 # if the head exists set cur to the next node
        while cur.next:                                 # loop through the rest of the chain looking for the end
            cur = cur.next
        cur.next = newNode                              # add the new node to the end

    # Removes an item at the index
    def remove(self, key) -> None:
        lastNode = self.head                            # previous node
        curNode = self.head                             # current node
        while key != curNode.key:                       # get to the node we are curious about
            lastNode = curNode
            if curNode.next == None:
                return None
            curNode = curNode.next
        if curNode == self.head:
            self.head = curNode.next
        else:
            lastNode.next = curNode.next                # pull the current node out of the linked list
        self.len -= 1                                   # remove the length by 1

    # returns the data at the index
    def get(self, key):
        if self.head:                                   # if the head exists
            cur: DerekLinkedListNode | None = self.head     # set the current searching for at the head
            while key != cur.key:                           # go through the chain until we hit the index were looking for
                if cur.next == None:
                    return
                cur = cur.next
            if isinstance(cur, DerekLinkedListNode):    # since we made it here the data were on is a LinkedListNode
                return cur.data                         # return the value out of the node
        else:
            return None