package pubsubclient

import "sync"

type subscriptions struct {
	sync.RWMutex
	subsByToken map[string]stringSet
}

func newSubscriptions() *subscriptions {
	return &subscriptions{
		subsByToken: make(map[string]stringSet),
	}
}

func (s subscriptions) listen(m *listenMsg) {
	s.Lock()
	defer s.Unlock()
	subs, ok := s.subsByToken[m.Data.AuthToken]
	if !ok {
		subs = make(stringSet)
		s.subsByToken[m.Data.AuthToken] = subs
	}
	for _, t := range m.Data.Topics {
		subs.Set(t)
	}
}

func (s subscriptions) unlisten(m *unlistenMsg) {
	s.Lock()
	defer s.Unlock()
	for token, subs := range s.subsByToken {
		for _, remove := range m.Data.Topics {
			subs.Unset(remove)
		}
		if len(subs) == 0 {
			delete(s.subsByToken, token)
		}
	}
}

func (s subscriptions) replay() []*listenMsg {
	var listens []*listenMsg
	for token, topics := range s.subsByToken {
		listen := &listenMsg{
			msg:   msg{listen},
			Nonce: randString(32),
			Data: topicList{
				AuthToken: token,
				Topics:    topics.All(),
			},
		}
		listens = append(listens, listen)
	}
	return listens
}

type stringSet map[string]struct{}

func (ss stringSet) Set(val string)   { ss[val] = struct{}{} }
func (ss stringSet) Unset(val string) { delete(ss, val) }
func (ss stringSet) All() []string {
	var out []string
	for s := range ss {
		out = append(out, s)
	}
	return out
}
