package syslog

// Handler handles syslog messages
type Handler interface {
	// Handle should return Message (maybe modified) for future processing by
	// other handlers or return nil. If Handle is called with nil message it
	// should complete all remaining work and properly shutdown before return.
	Handle(SyslogMessage) SyslogMessage
}

// BaseHandler is designed to simplify the creation of real handlers. It
// implements Handler interface using nonblocking queuing of messages and
// simple message filtering.
type BaseHandler struct {
	queue  chan SyslogMessage
	end    chan struct{}
	filter func(SyslogMessage) bool
	ft     bool
}

// NewBaseHandler creates BaseHandler using a specified filter. If filter is nil
// or if it returns true messages are passed to BaseHandler internal queue
// (of qlen length). If filter returns false or ft is true messages are returned
// to server for future processing by other handlers.
func NewBaseHandler(qlen int, filter func(SyslogMessage) bool, ft bool) *BaseHandler {
	return &BaseHandler{
		queue:  make(chan SyslogMessage, qlen),
		end:    make(chan struct{}),
		filter: filter,
		ft:     ft,
	}
}

// Handle inserts m in an internal queue. It immediately returns even if
// queue is full. If m == nil it closes queue and waits for End method call
// before return.
func (h *BaseHandler) Handle(m SyslogMessage) SyslogMessage {
	if m == nil {
		close(h.queue) // signal that there is no more messages for processing
		<-h.end        // wait for handler shutdown
		return nil
	}
	if h.filter != nil && !h.filter(m) {
		// m doesn't match the filter
		return m
	}
	// Try queue m
	select {
	case h.queue <- m:
	default:
	}
	if h.ft {
		return m
	}
	return nil
}

// Get returns first message from internal queue. It waits for message if queue
// is empty. It returns nil if there is no more messages to process and handler
// should shutdown.
func (h *BaseHandler) Get() SyslogMessage {
	m, ok := <-h.queue
	if ok {
		return m
	}
	return nil
}

// Queue returns the BaseHandler internal queue as a read-only channel. You can use
// it directly, especially if your handler needs to select from multiple channels
// or have to work without blocking. You need to check if this channel is closed by
// sender and properly shutdown in this case.
func (h *BaseHandler) Queue() <-chan SyslogMessage {
	return h.queue
}

// End signals the server that the handler properly shutdown. You need to call End
// only if Get has returned nil before.
func (h *BaseHandler) End() {
	close(h.end)
}
