
from linked_list_example import Node, LinkedList
from typing import Any


class LinkedList2(LinkedList):
   """ Linked list with iteration, indexing, and slicing. """


   def __iter__(self):
      """ Gets an iterator for the list. """
      return ListIter(self)


   def __getitem__(self, key: int or slice) -> Any:
      """ Gets the value at a specified index or a list for a specified slice.

   	:param key: the index or slice of interest.

   	:return: The value or list at the specified index or for the specified slice.
   	:rtype: Any

   	:raises IndexError: If the key is an int and is out of range.

   	:raises TypeError: If the key is not an int or slice. """
      if isinstance(key, int):
         start = key
         if start < 0:
            start += self.size
         if start < 0 or start >= self.size:
            raise IndexError(f"Index ({key}) out of range.")
      elif isinstance(key, slice):
         step: int = 1 if key.step is None else key.step
         start = key.start
         if start is None:
            start = 0 if step > 0 else self.size - 1
         elif start < 0:
            start += self.size
         end: int = key.stop
         if end is None:
            end = self.size if step > 0 else -1
         elif end < 0:
            end += self.size

         if step < 0:
            tmp: int = start + 1
            start = end + ((start - end) % -step)
            if start == end:
               start -= step
            end = tmp
         if start >= end:
            return []
      else:
         raise TypeError("Invalid key type")
      node: Node = self.head
      for i in range(0, start):
         node = node.next
      if isinstance(key, int):
         return node.value
      result = []
      abs_step: int = abs(step)
      print(f"start {start} end {end}")
      for i in range(start, end, abs_step):
         result.append(node.value)
         past_end: bool = False
         for k in range(0, abs_step):
            if node.next is None:
               past_end = True
               break
            node = node.next
            if past_end:
               break
      if step < 0:
         result.reverse()
      return result


class ListIter:
   """ Iterator for LinkedList2. """


   def __init__(self, list1: LinkedList):
      self.list1 = list1
      self.next = list1.head


   def __iter__(self):
      return self


   def __next__(self):
      if self.next is None:
         raise StopIteration
      result = self.next.value
      self.next = self.next.next
      return result


if __name__ == '__main__':
   list1: LinkedList2 = LinkedList2()
   for v in [10, 5, 4, 12, 1, 9, 7, 8]:
      list1.add(v)

   print(f"values: {list1} : {list1[0:4]}")
   list1.selection_sort()
   print(f"sorted values: {list1}")
