package syncz

import (
	"sync"
)

// SyncSubscriptions implements a simple synchronous pub-sub with multiple publishers and multiple consumers.
// Callback functions are all called sequentially by the publisher.
// Make sure this does not create an undesired bottleneck in your code.
type SyncSubscriptions[E any] struct {
	mu    sync.Mutex
	subs  map[uint64]func(E)
	index uint64
}

func NewSyncSubscriptions[E any]() *SyncSubscriptions[E] {
	return &SyncSubscriptions[E]{
		subs: make(map[uint64]func(E)),
	}
}

func (s *SyncSubscriptions[E]) On(cb func(E)) func() {
	idx := s.add(cb)
	return func() {
		s.remove(idx)
	}
}

// Len returns the number of subscriptions.
func (s *SyncSubscriptions[E]) Len() int {
	s.mu.Lock()
	defer s.mu.Unlock()

	return len(s.subs)
}

// Dispatch dispatches the given event to all added subscriptions.
func (s *SyncSubscriptions[E]) Dispatch(e E) {
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, cb := range s.subs {
		cb(e)
	}
}

func (s *SyncSubscriptions[E]) add(cb func(E)) uint64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	idx := s.index
	s.index++
	s.subs[idx] = cb
	return idx
}

func (s *SyncSubscriptions[E]) remove(idx uint64) {
	s.mu.Lock()
	defer s.mu.Unlock()
	delete(s.subs, idx)
}
