.{log, memory} := @use("stn")

reverse := fn(str: []u8): void {
	if str.len == 0 return;
	j := str.len - 1
	i := 0
	temp := @as(u8, 0)
	loop if i < j {
		temp = str[i]
		str[i] = str[j]
		str[j] = temp
		i += 1
		j -= 1
	} else return
}

$equals := fn(lhs: []u8, rhs: []u8): bool {
	return lhs.ptr == rhs.ptr & lhs.len == rhs.len
}

clear := fn(str: []u8): void {
	i := 0
	loop if i == str.len break else {
		str[i] = 0
		i += 1
	}
}

split_once := fn(haystack: []u8, needle: @Any()): ?[]u8 {
	T := @TypeOf(needle)
	if T == []u8 {
		if needle.len == 0 return null
		i := 0
		loop if i == haystack.len return null else {
			if haystack[i] == needle[0] {
				h := i
				n := 0

				loop {
					n += 1
					h += 1
					if needle[n] == 0 {
						return haystack[h..]
					} else if haystack[h] == 0 | haystack[h] != needle[n] {
						break
					}
				}
			}
			i += 1
		}
	} else if T == u8 {
		i := 0
		loop if haystack[i] == needle return haystack[i..] else if i == haystack.len return null else i += 1
	} else {
		@error("Type of needle must be []u8 or u8.")
	}
}

SplitIter := fn($T: type): type {
	return struct {
		str: []u8,
		needle: T,
		done: bool,

		next := fn(self: ^Self): ?[]u8 {
			if self.done return null;

			if @TypeOf(self.needle) == u8 {
				i := 0
				loop if i == self.str.len {
					self.done = true
					return self.str
				} else if self.str[i] == self.needle {
					result := self.str[..i]
					self.str = self.str[i + 1..]
					return result
				} else {
					i += 1
				}
			} else if @TypeOf(self.needle) == []u8 {
				if self.needle.len == 0 return null
				i := 0
				loop if i == self.str.len {
					self.done = true
					return self.str
				} else if self.str[i] == self.needle[0] {
					h := i
					n := 0

					loop {
						n += 1
						h += 1

						if n == self.needle.len {
							result := self.str[..i]
							self.str = self.str[h..]
							return result
						} else if h == self.str.len | self.str[h] != self.needle[n] {
							break
						}
					}
				}
				i += 1
			}

			self.done = true
			return self.str
		}
	}
}

split := fn(iter: []u8, needle: @Any()): SplitIter(@TypeOf(needle)) {
	T := @TypeOf(needle)
	if T != []u8 & T != u8 {
		@error("Type of needle must be []u8 or u8.")
	}
	return .(iter, needle, false)
}

find_once := fn(haystack: ^u8, needle: @Any()): ?uint {
	return @bitcast(@inline(split_once, haystack, needle) - haystack)
}

count := fn(haystack: []u8, needle: @Any()): uint {
	T := @TypeOf(needle)
	c := 0
	if T == u8 {
		i := 0
		loop if haystack[i] == needle {
			c += 1
			i += 1
		} else if i == haystack.len return c else i += 1
	} else if T == []u8 {
		i := 0
		loop if i == haystack.len return c else {
			if haystack[i] == needle[0] {
				h := i
				n := 0

				loop {
					n += 1
					h += 1
					if n == needle.len {
						c += 1
						i = h - 1
						break
					} else if h == haystack.len | haystack[h] != needle[n] {
						break
					}
				}
			}
			i += 1
		}
	} else {
		@error("Type of needle must be []u8 or u8.")
	}
}

left_trim := fn(str: []u8, sub: []u8): []u8 {
	i := 0
	if str[0] == sub[0] {
		loop if i == sub.len {
			return str[i..str.len]
		} else if str[i] != sub[i] | i == str.len {
			return str
		} else {
			i += 1
		}
	}
}