package pubsub

import (
	"sync"
)

type PubSub struct {
	mu     sync.Mutex
	next   chan item
	closed bool
}

type item struct {
	value interface{}
	next  chan item
}

type Subscription struct {
	next   chan item
	Values chan interface{}
	mu     sync.Mutex
	closed chan struct{}
}

func NewPubSub() (ret *PubSub) {
	return new(PubSub)
}

func (me *PubSub) init() {
	me.next = make(chan item, 1)
}

func (me *PubSub) lazyInit() {
	me.mu.Lock()
	defer me.mu.Unlock()
	if me.closed {
		return
	}
	if me.next == nil {
		me.init()
	}
}

func (me *PubSub) Publish(v interface{}) {
	me.lazyInit()
	next := make(chan item, 1)
	i := item{v, next}
	me.mu.Lock()
	if !me.closed {
		me.next <- i
		me.next = next
	}
	me.mu.Unlock()
}

func (me *Subscription) Close() {
	me.mu.Lock()
	defer me.mu.Unlock()
	select {
	case <-me.closed:
	default:
		close(me.closed)
	}
}

func (me *Subscription) runner() {
	defer close(me.Values)
	for {
		select {
		case i, ok := <-me.next:
			if !ok {
				me.Close()
				return
			}
			// Send the value back into the channel for someone else. This
			// won't block because the channel has a capacity of 1, and this
			// is currently the only copy of this value being sent to this
			// channel.
			me.next <- i
			// The next value comes from the channel given to us by the value
			// we just got.
			me.next = i.next
			select {
			case me.Values <- i.value:
			case <-me.closed:
				return
			}
		case <-me.closed:
			return
		}
	}
}

func (me *PubSub) Subscribe() (ret *Subscription) {
	me.lazyInit()
	ret = &Subscription{
		closed: make(chan struct{}),
		Values: make(chan interface{}),
	}
	me.mu.Lock()
	ret.next = me.next
	me.mu.Unlock()
	go ret.runner()
	return
}

func (me *PubSub) Close() {
	me.mu.Lock()
	defer me.mu.Unlock()
	if me.closed {
		return
	}
	if me.next != nil {
		close(me.next)
	}
	me.closed = true
}
