package mhayaActor

import (
	"sync/atomic"
	"unsafe"
)

type (
	queue struct {
		head, tail *queueNode
		C          chan int32
		count      int32
	}

	queueNode struct {
		next *queueNode
		val  interface{}
	}
)

func newQueue() queue {
	stub := &queueNode{}
	q := queue{
		head:  stub,
		tail:  stub,
		C:     make(chan int32, 1),
		count: 0,
	}
	return q
}

func (p *queue) Push(v interface{}) {
	if v == nil {
		return
	}

	n := new(queueNode)
	n.val = v
	// current producer acquires head node
	prev := (*queueNode)(atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&p.head)), unsafe.Pointer(n)))

	// release node to consumer
	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&prev.next)), unsafe.Pointer(n))

	p._setCount(1)
}

func (p *queue) Pop() interface{} {
	tail := p.tail
	next := (*queueNode)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next)))) // acquire
	if next != nil {
		p.tail = next
		v := next.val
		next.val = nil
		p._setCount(-1)
		return v
	}

	return nil
}

func (p *queue) Empty() bool {
	tail := p.tail
	next := (*queueNode)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next))))
	return next == nil
}

func (p *queue) Count() int32 {
	return atomic.LoadInt32(&p.count)
}

func (p *queue) _setCount(delta int32) {
	count := atomic.AddInt32(&p.count, delta)
	if count > 0 {
		select {
		case p.C <- count:
		default:
		}
	}
}

func (p *queue) Destroy() {
	close(p.C)
	p.head = nil
	p.tail = nil
	p.count = 0
}
