package state

import (
	"sync"
)

// NotifyGroup is used to allow a simple notification mechanism.
// Channels can be marked as waiting, and when notify is invoked,
// all the waiting channels get a message and are cleared from the
// notify list.
type NotifyGroup struct {
	l      sync.Mutex
	notify map[chan struct{}]struct{}
}

// Notify will do a non-blocking send to all waiting channels, and
// clear the notify list
func (n *NotifyGroup) Notify() {
	n.l.Lock()
	defer n.l.Unlock()
	for ch := range n.notify {
		select {
		case ch <- struct{}{}:
		default:
		}
	}
	n.notify = nil
}

// Wait adds a channel to the notify group
func (n *NotifyGroup) Wait(ch chan struct{}) {
	n.l.Lock()
	defer n.l.Unlock()
	if n.notify == nil {
		n.notify = make(map[chan struct{}]struct{})
	}
	n.notify[ch] = struct{}{}
}

// Clear removes a channel from the notify group
func (n *NotifyGroup) Clear(ch chan struct{}) {
	n.l.Lock()
	defer n.l.Unlock()
	if n.notify == nil {
		return
	}
	delete(n.notify, ch)
}

// WaitCh allocates a channel that is subscribed to notifications
func (n *NotifyGroup) WaitCh() chan struct{} {
	ch := make(chan struct{}, 1)
	n.Wait(ch)
	return ch
}

// Empty checks if there are no channels to notify
func (n *NotifyGroup) Empty() bool {
	n.l.Lock()
	defer n.l.Unlock()
	return len(n.notify) == 0
}
