// test generics function that return generics struct
pub struct Optional[T] {
mut:
	value T
	some  bool
}

pub fn new_some[T](value T) Optional[T] {
	return Optional[T]{
		value: value
		some:  true
	}
}

pub fn some[T](opt Optional[T]) bool {
	return opt.some
}

pub fn get[T](opt Optional[T]) T {
	return opt.value
}

pub fn set[T](mut opt Optional[T], value T) {
	opt.value = value
	opt.some = true
}

fn test_generics_fn_return_generics_struct() {
	mut o := new_some[int](23)
	println(some[int](o))
	assert some[int](o) == true
	set[int](mut o, 42)
	println(get[int](o))
	assert get[int](o) == 42
}

// test generics method that return generics struct
pub struct Foo {
	foo int
}

pub fn (f Foo) new_some[T](value T) Optional[T] {
	return Optional[T]{
		value: value
		some:  true
	}
}

pub fn (f Foo) some[T](opt Optional[T]) bool {
	return opt.some
}

pub fn (f Foo) get[T](opt Optional[T]) T {
	return opt.value
}

pub fn (f Foo) set[T](mut opt Optional[T], value T) {
	opt.value = value
	opt.some = true
}

fn test_generics_method_return_generics_struct() {
	foo := Foo{}
	mut o := foo.new_some[int](23)
	println(foo.some[int](o))
	assert foo.some[int](o) == true
	foo.set[int](mut o, 42)
	println(foo.get[int](o))
	assert foo.get[int](o) == 42
}

// test generic struct str()
pub struct ArrayIterator[T] {
	data []T
mut:
	index int
}

pub fn iter[T](arr []T) ArrayIterator[T] {
	return ArrayIterator[T]{
		data:  arr
		index: 11
	}
}

fn test_generics_with_generics_struct_string() {
	data := ['foo', 'bar']
	it := iter[string](data)
	println(it)
	ret := '${it}'
	assert ret.contains('ArrayIterator[string]{')
	assert ret.contains("data: ['foo', 'bar']")
	assert ret.contains('index: 11')
}

fn test_generics_struct_insts_to_concrete() {
	ai := ArrayIterator[int]{
		data:  [11, 22]
		index: 22
	}
	println(ai)
	ret := '${ai}'
	assert ret.contains('ArrayIterator[int]{')
	assert ret.contains('data: [11, 22]')
	assert ret.contains('index: 22')
}

struct Iterator[T] {
	data []T
}

pub fn (mut i Iterator[T]) next[T]() ?T {
	return i.data[0]
}

pub fn iter_data[T](data []T) Iterator[T] {
	return Iterator[T]{
		data: data
	}
}

fn test_generics_return_generic_struct_from_fn() {
	mut it := iter_data[int]([1, 2, 3])
	println(it.next() or { -1 })
	assert '${it.next()}' == 'Option(1)'
}

struct ListNode[T] {
pub mut:
	val  T
	next &ListNode[T] = unsafe { nil }
}

fn (mut node ListNode[T]) test() &ListNode[T] {
	return node.next
}

fn test_generics_return_generic_struct_field() {
	mut node1 := &ListNode[int]{100, unsafe { nil }}
	mut node2 := &ListNode[int]{200, unsafe { nil }}
	node1.next = node2
	ret := node1.test()
	println(ret)
	assert ret.val == 200
}
