package main

import "fmt"

// Queue is a generic FIFO (First In, First Out) data structure
type Queue[T any] struct {
	items []T
}

// NewQueue creates a new empty queue
func NewQueue[T any]() *Queue[T] {
	return &Queue[T]{
		items: make([]T, 0),
	}
}

// Enqueue adds an item to the back of the queue
func (q *Queue[T]) Enqueue(item T) {
	q.items = append(q.items, item)
}

// Dequeue removes and returns the item from the front of the queue
// Returns the item and a boolean indicating success
func (q *Queue[T]) Dequeue() (T, bool) {
	var zero T
	if len(q.items) == 0 {
		return zero, false
	}

	item := q.items[0]
	clear(q.items[:1])
	q.items = q.items[1:] // Remove the first element
	return item, true
}

// Peek returns the front item without removing it
// Returns the item and a boolean indicating success
func (q *Queue[T]) Peek() (T, bool) {
	var zero T
	if len(q.items) == 0 {
		return zero, false
	}

	return q.items[0], true
}

// Size returns the number of items in the queue
func (q *Queue[T]) Size() int {
	return len(q.items)
}

// IsEmpty returns true if the queue is empty
func (q *Queue[T]) IsEmpty() bool {
	return len(q.items) == 0
}

func main() {
	// Test with integers
	fmt.Println("=== Testing Integer Queue ===")
	intQueue := NewQueue[int]()

	// Add some numbers
	intQueue.Enqueue(10)
	intQueue.Enqueue(20)
	intQueue.Enqueue(30)

	fmt.Printf("Queue size: %d\n", intQueue.Size())
	fmt.Printf("Is empty: %t\n", intQueue.IsEmpty())

	// Peek at front
	if front, ok := intQueue.Peek(); ok {
		fmt.Printf("Front item: %d\n", front)
	}

	// Dequeue all items
	for !intQueue.IsEmpty() {
		if item, ok := intQueue.Dequeue(); ok {
			fmt.Printf("Dequeued: %d\n", item)
		}
	}

	fmt.Printf("After dequeuing all items - Size: %d, IsEmpty: %t\n", intQueue.Size(), intQueue.IsEmpty())

	// Test with strings
	fmt.Println("\n=== Testing String Queue ===")
	stringQueue := NewQueue[string]()

	// Add some strings
	stringQueue.Enqueue("first")
	stringQueue.Enqueue("second")
	stringQueue.Enqueue("third")

	fmt.Printf("Queue size: %d\n", stringQueue.Size())

	// Dequeue and print
	for !stringQueue.IsEmpty() {
		if item, ok := stringQueue.Dequeue(); ok {
			fmt.Printf("Dequeued: %s\n", item)
		}
	}

	// Test dequeue from empty queue
	fmt.Println("\n=== Testing Empty Queue ===")
	emptyQueue := NewQueue[int]()
	if _, ok := emptyQueue.Dequeue(); !ok {
		fmt.Println("Cannot dequeue from empty queue - handled correctly!")
	}

	if _, ok := emptyQueue.Peek(); !ok {
		fmt.Println("Cannot peek empty queue - handled correctly!")
	}
}
