package common

import (
	"container/list"
	"sync"
)

type Queue[T any] struct {
	l    *list.List
	lock *sync.RWMutex
}

func NewQueue[T any](threadSafe bool) *Queue[T] {
	qu := &Queue[T]{
		l: list.New(),
	}
	if threadSafe {
		qu.lock = new(sync.RWMutex)
	}
	return qu
}

func (qu *Queue[T]) In(d T) {
	if qu.lock != nil {
		qu.lock.Lock()
		defer qu.lock.Unlock()
	}
	qu.l.PushBack(d)
}

func (qu *Queue[T]) Out() T {
	if qu.lock != nil {
		qu.lock.Lock()
		defer qu.lock.Unlock()
	}
	d := qu.l.Front()
	if d != nil {
		qu.l.Remove(d)
		return d.Value.(T)
	}
	var t T
	return t
}

func (qu *Queue[T]) Len() int {
	if qu.lock != nil {
		qu.lock.RLock()
		defer qu.lock.RUnlock()
	}
	return qu.l.Len()
}

func (qu *Queue[T]) PeakFront() *T {
	if qu.lock != nil {
		qu.lock.RLock()
		defer qu.lock.RUnlock()
	}
	e := qu.l.Front()
	if e == nil {
		return nil
	}
	d := e.Value.(T)
	return &d
}

func (qu *Queue[T]) PeakBack() *T {
	if qu.lock != nil {
		qu.lock.RLock()
		defer qu.lock.RUnlock()
	}
	e := qu.l.Front()
	if e == nil {
		return nil
	}
	d := e.Value.(T)
	return &d
}

/*
fn return false to stop
*/
func (qu *Queue[T]) Traverse(fromHead bool, fn func(index int, d T) (goToNext bool)) {
	if qu.lock != nil {
		qu.lock.RLock()
		defer qu.lock.RUnlock()
	}

	if fromHead {
		var i int
		for cur := qu.l.Front(); cur != nil; cur = cur.Next() {
			if !fn(i, cur.Value.(T)) {
				return
			}
			i++
		}
	} else {
		var i = qu.l.Len()
		for cur := qu.l.Back(); cur != nil; cur = cur.Prev() {
			i--
			if !fn(i, cur.Value.(T)) {
				return
			}
		}
	}
}

/*
fn return true to delete
*/
func (qu *Queue[T]) TraverseDelete(fromHead bool, fn func(index int, d T) (delete bool)) {
	if qu.lock != nil {
		qu.lock.Lock()
		defer qu.lock.Unlock()
	}

	if fromHead {
		var i int
		for cur := qu.l.Front(); cur != nil; {
			if fn(i, cur.Value.(T)) {
				next := cur.Next()
				qu.l.Remove(cur)
				cur = next
			} else {
				cur = cur.Next()
			}
			i++
		}
	} else {
		var i = qu.l.Len()
		for cur := qu.l.Back(); cur != nil; {
			i--
			if fn(i, cur.Value.(T)) {
				next := cur.Prev()
				qu.l.Remove(cur)
				cur = next
			} else {
				cur = cur.Prev()
			}
		}
	}
}

/*
clear all element
*/
func (qu *Queue[T]) Reset() {
	if qu.lock != nil {
		qu.lock.Lock()
		defer qu.lock.Unlock()
	}
	qu.l.Init()
}
