// Package signal provides mechanism for notifying multiple listeners when something happened.
package signal

import (
	"context"
	"sync"
)

// A Signal is used to let multiple listeners know when something happened.
type Signal struct {
	mu  sync.Mutex
	chs map[chan context.Context]struct{}
}

// New creates a new Signal.
func New() *Signal {
	return &Signal{
		chs: make(map[chan context.Context]struct{}),
	}
}

// Broadcast signals all the listeners. Broadcast never blocks.
func (s *Signal) Broadcast(ctx context.Context) {
	s.mu.Lock()
	for ch := range s.chs {
		select {
		case ch <- ctx:
		default:
		}
	}
	s.mu.Unlock()
}

// Bind creates a new listening channel bound to the signal. The channel used has a size of 1
// and any given broadcast will signal at least one event, but may signal more than one.
func (s *Signal) Bind() chan context.Context {
	ch := make(chan context.Context, 1)
	s.mu.Lock()
	s.chs[ch] = struct{}{}
	s.mu.Unlock()
	return ch
}

// Unbind stops the listening channel bound to the signal.
func (s *Signal) Unbind(ch chan context.Context) {
	s.mu.Lock()
	delete(s.chs, ch)
	s.mu.Unlock()
}