package collection

import (
	. "gitea.zaclys.com/bvaudour/gob/option"
)

// Queue représente une file d’élément.
// Une file est une collection où les éléments suivant la logique “premier arrivé, premier servi“.
type Queue[T any] struct {
	first *chain[T]
	last  *chain[T]
	size  uint
}

// Len retourne le nombre d’éléments de la file.
func (q Queue[T]) Len() uint {
	return q.size
}

func (q *Queue[T]) push(e T) {
	var (
		isEmpty = q.size == 0
		oldLast Option[*chain[T]]
	)

	if !isEmpty {
		oldLast = Some(q.last)
	}
	q.last = nextChain(e, oldLast)

	if isEmpty {
		q.first = q.last
	}
	q.size++
}

// Push ajoute tous les éléments d’entrée dans la file.
func (q *Queue[T]) Push(elems ...T) {
	for _, e := range elems {
		q.push(e)
	}
}

// Concatenate concatène deux files.
func (q1 *Queue[T]) Concatenate(q2 *Queue[T]) {
	c, l := q2.first, q2.size
	for i := uint(0); i < l; i++ {
		q1.push(c.value)
		c = c.next
	}
}

// Pop dépile le premier élément de la file et le retourne.
func (q *Queue[T]) Pop() (e Option[T]) {
	if q.size > 0 {
		e = Some(q.first.value)
		q.first = q.first.next
		q.size--
		if q.size == 0 {
			q.last = q.first
		}
	}

	return
}

// ToSlice convertit une file en slice.
func (q *Queue[T]) ToSlice() (sl []T) {
	sl = make([]T, q.size)
	c := q.first
	for i := uint(0); i < q.size; i++ {
		sl[i] = c.value
		c = c.next
	}

	return
}

// ToStack convertit une file en pile.
func (q *Queue[T]) ToStack() (s *Stack[T]) {
	s = &Stack[T]{
		first: q.first,
		size:  q.size,
	}

	return
}

// NewQueue retourne une file initialisée à partir des données d’entrées.
func NewQueue[T any](elems ...T) (q *Stack[T]) {
	q = new(Stack[T])
	q.Push(elems...)

	return
}
