/*
Package topic provides implementations of server-side topics.
*/
package topic

import (
	"container/list"

	"github.com/go-stomp/stomp/v3/frame"
)

// A Topic is used for broadcasting to subscribed clients.
// In contrast to a queue, when a message is sent to a topic,
// that message is transmitted to all subscribed clients.
type Topic struct {
	destination string
	subs        *list.List
}

// Create a new topic -- called from the topic manager only.
func newTopic(destination string) *Topic {
	return &Topic{
		destination: destination,
		subs:        list.New(),
	}
}

// Subscribe adds a subscription to a topic. Any message sent to the
// topic will be transmitted to the subscription's client until
// unsubscription occurs.
func (t *Topic) Subscribe(sub Subscription) {
	t.subs.PushBack(sub)
}

// Unsubscribe causes a subscription to be removed from the topic.
func (t *Topic) Unsubscribe(sub Subscription) {
	for e := t.subs.Front(); e != nil; e = e.Next() {
		if sub == e.Value.(Subscription) {
			t.subs.Remove(e)
			return
		}
	}
}

// Enqueue send a message to the topic. All subscriptions receive a copy
// of the message.
func (t *Topic) Enqueue(f *frame.Frame) {
	switch t.subs.Len() {
	case 0:
	// no subscription, so do nothing

	case 1:
		// only one subscription, so can send the frame
		// without copying
		sub := t.subs.Front().Value.(Subscription)
		sub.SendTopicFrame(f)

	default:
		// more than one subscription, send clone for
		// all subscriptions except the last, which can
		// have the frame without copying
		for e := t.subs.Front(); e != nil; e = e.Next() {
			sub := e.Value.(Subscription)
			if e.Next() == nil {
				// the last in the list, send the frame
				// without copying
				sub.SendTopicFrame(f)
			} else {
				sub.SendTopicFrame(f.Clone())
			}
		}
	}
}
