package main

import "fmt"

// section: zero-values
// Stack is a generic stack data structure
type Stack[T any] struct {
	items []T
}

// NewStack creates a new stack
func NewStack[T any]() *Stack[T] {
	return &Stack[T]{items: make([]T, 0)}
}

// Push adds an item to the stack
func (s *Stack[T]) Push(item T) {
	s.items = append(s.items, item)
}

// Pop removes and returns the top item
func (s *Stack[T]) Pop() (T, bool) {
	if len(s.items) == 0 {
		var zero T
		return zero, false
	}
	idx := len(s.items) - 1
	item := s.items[idx]
	clear(s.items[idx:])
	s.items = s.items[:idx]
	return item, true
}

func main() {
	// When creating zero values of generic types,
	// we need explicit type instantiation

	// Creating zero values requires explicit types:
	var intStack Stack[int]       // Zero value of Stack[int]
	var stringStack Stack[string] // Zero value of Stack[string]

	fmt.Printf("Zero int stack: %+v\n", intStack)
	fmt.Printf("Zero string stack: %+v\n", stringStack)

	// Using constructor functions:
	numberStack := NewStack[int]()
	wordStack := NewStack[string]()

	// Use the stacks:
	numberStack.Push(42)
	wordStack.Push("hello")

	num, ok := numberStack.Pop()
	fmt.Printf("Popped number: %d (ok: %t)\n", num, ok)

	word, ok := wordStack.Pop()
	fmt.Printf("Popped word: %s (ok: %t)\n", word, ok)
}

// section: zero-values
