package utils

import (
	"sync/atomic"
)

type RefCount struct {
	_ NoCopy

	refCount uint32
	zeroCh   chan struct{}
}

func NewRefCounter() *RefCount {
	rc := &RefCount{
		refCount: 1,
		zeroCh:   make(chan struct{}),
	}
	return rc
}

func (rc *RefCount) Zero() <-chan struct{} {
	return rc.zeroCh
}

func (rc *RefCount) Add() {
	// We add before checking to ensure proper ordering
	newV := atomic.AddUint32(&rc.refCount, 1)
	if newV == 1 {
		// If it was 0 before we added, that means we're incrementing after a close
		// This is a programming error
		panic("RefCount.Add() called after count reached zero")
	}
}

func (rc *RefCount) Sub() {
	// First read the current value
	for {
		current := atomic.LoadUint32(&rc.refCount)
		if current == 0 {
			// Already at zero, channel should be closed
			return
		}

		// Try to decrement, but only if the value hasn't changed
		if atomic.CompareAndSwapUint32(&rc.refCount, current, current-1) {
			if current == 1 { // Was this the last reference?
				close(rc.zeroCh)
			}
			return
		}
		// If CAS failed, someone else modified the count, try again
	}
}