module datatypes

pub struct ListNode[T] {
mut:
	data T
	next &ListNode[T] = unsafe { 0 }
}

pub struct LinkedList[T] {
mut:
	head &ListNode[T] = unsafe { 0 }
	len  int
	// Internal iter pointer for allowing safe modification
	// of the list while iterating. TODO: use an option
	// instead of a pointer to determine if it is initialized.
	iter &ListIter[T] = unsafe { 0 }
}

// is_empty checks if the linked list is empty
pub fn (list LinkedList[T]) is_empty() bool {
	return list.len == 0
}

// len returns the length of the linked list
pub fn (list LinkedList[T]) len() int {
	return list.len
}

// first returns the first element of the linked list
pub fn (list LinkedList[T]) first() !T {
	return if !list.is_empty() { list.head.data } else { error('Linked list is empty') }
}

// last returns the last element of the linked list
pub fn (list LinkedList[T]) last() !T {
	if unsafe { list.head == 0 } {
		return error('Linked list is empty')
	} else {
		mut node := list.head
		for unsafe { node.next != 0 } {
			node = node.next
		}
		return node.data
	}
}

// index returns the element at the given index of the linked list
pub fn (list LinkedList[T]) index(idx int) !T {
	if unsafe { list.head == 0 } {
		return error('Linked list is empty')
	} else {
		mut node := list.head
		mut iterations := 0
		for unsafe { node.next != 0 } && iterations < idx {
			node = node.next
			iterations++
		}
		if iterations == idx {
			return node.data
		} else {
			return error('Index ${idx} != iterations: ${iterations}')
		}
	}
}

// push adds an element to the end of the linked list
pub fn (mut list LinkedList[T]) push(item T) {
	new_node := &ListNode[T]{
		data: item
	}
	if unsafe { list.head == 0 } {
		// first node case
		list.head = new_node
	} else {
		mut node := list.head
		for unsafe { node.next != 0 } {
			node = node.next
		}
		node.next = new_node
	}
	list.len += 1
}

// push adds an array of elements to the end of the linked list
pub fn (mut list LinkedList[T]) push_many(elements []T) {
	for v in elements {
		list.push(v)
	}
}

// pop removes the last element of the linked list
pub fn (mut list LinkedList[T]) pop() !T {
	if unsafe { list.head == 0 } {
		return error('Linked list is empty')
	}
	mut node := list.head
	mut to_return := unsafe { node.data }
	if unsafe { node.next == 0 } {
		// first node case
		// set to null
		list.head = unsafe { nil }
	} else {
		for unsafe { node.next.next != 0 } {
			node = node.next
		}
		to_return = unsafe { node.next.data }
		// set to null
		node.next = unsafe { nil }
	}
	list.len -= 1
	return to_return
}

// shift removes the first element of the linked list
pub fn (mut list LinkedList[T]) shift() !T {
	if unsafe { list.head == 0 } {
		return error('Linked list is empty')
	} else {
		list.len -= 1
		node := list.head
		list.head = node.next
		return node.data
	}
}

// insert adds an element to the linked list at the given index
pub fn (mut list LinkedList[T]) insert(idx int, item T) ! {
	if idx < 0 || idx > list.len {
		return error('Index ${idx} out of bounds [0..${list.len}]')
	} else if list.len == 0 {
		list.push(item)
	} else {
		list.len += 1
		mut node := list.head

		if idx == 0 {
			// first node case
			list.head = &ListNode[T]{
				data: item
				next: node
			}
		} else {
			for i := 0; i < idx - 1; i++ {
				node = node.next
			}
			node.next = &ListNode[T]{
				data: item
				next: node.next
			}
		}
	}
}

// prepend adds an element to the beginning of the linked list (equivalent to insert(0, item))
pub fn (mut list LinkedList[T]) prepend(item T) {
	list.insert(0, item) or {}
}

// str returns a string representation of the linked list
pub fn (list LinkedList[T]) str() string {
	return list.array().str()
}

// array returns a array representation of the linked list
pub fn (list LinkedList[T]) array() []T {
	mut result_array := []T{cap: list.len}
	mut node := list.head
	for unsafe { node != 0 } {
		result_array << node.data
		node = node.next
	}
	return result_array
}

// next implements the iteration interface to use LinkedList
// with V's `for` loop syntax.
pub fn (mut list LinkedList[T]) next() ?T {
	if list.iter == unsafe { nil } {
		// initialize new iter object
		list.iter = &ListIter[T]{
			node: list.head
		}
		return list.next()
	}
	if list.iter.node == unsafe { nil } {
		list.iter = unsafe { nil }
		return none
	}
	defer {
		list.iter.node = list.iter.node.next
	}
	return list.iter.node.data
}

// iterator returns a new iterator instance for the `list`.
pub fn (mut list LinkedList[T]) iterator() ListIter[T] {
	return ListIter[T]{
		node: list.head
	}
}

// ListIter[T] is an iterator for LinkedList.
// It can be used with V's `for x in iter {` construct.
// One list can have multiple independent iterators, pointing to different positions/places in the list.
// An iterator instance always traverses the list from start to finish.
pub struct ListIter[T] {
mut:
	node &ListNode[T] = unsafe { 0 }
}

// next returns the next element of the list, or `none` when the end of the list is reached.
// It is called by V's `for x in iter{` on each iteration.
pub fn (mut iter ListIter[T]) next() ?T {
	if iter.node == unsafe { nil } {
		return none
	}
	res := unsafe { iter.node.data }
	iter.node = iter.node.next
	return res
}
