package lightstep

import (
	"log"
	"sync"
	"sync/atomic"
)

func init() {
	SetGlobalEventHandler(NewEventLogOneError())
}

var eventHandler atomic.Value

// An EventHandler can be registered with SetGlobalEventHandler to
type EventHandler func(Event)

// emitEvent is a thread-safe function for emiting tracer events.
func emitEvent(event Event) {
	handler := eventHandler.Load().(EventHandler)
	handler(event)
}

// SetGlobalEventHandler sets a global handler to receive tracer events as they occur. Events
// may be emitted by the tracer, or by calls to static functions in this package.
// It is suggested that you set your EventHandler before starting your tracer,
// but it is safe to set a new handler at any time. Setting a new handler removes
// the previous one.
//
// The EventHandler is called synchronously – do not block in your handler as it
// may interfere with the tracer. If no EventHandler handler is set, a
// LogOnceOnError handler is set by default.
//
// NOTE: Event handling is for reporting purposes only. It is not intended as a
// mechanism for controling or restarting the tracer. Connection issues, retry
// logic, and other transient errors are handled internally by the tracer.
func SetGlobalEventHandler(handler EventHandler) {
	eventHandler.Store(handler)
}

/*
	SetGlobalEventHandler Handlers
*/

// NewEventLogger logs events using the standard go logger.
func NewEventLogger() EventHandler {
	return logOnEvent
}

func logOnEvent(event Event) {
	switch event := event.(type) {
	case ErrorEvent:
		log.Println("LS Tracer error: ", event)
	default:
		log.Println("LS Tracer event: ", event)
	}
}

// NewEventLogOneError logs the first error event that occurs.
func NewEventLogOneError() EventHandler {
	logger := logOneError{}
	return logger.OnEvent
}

type logOneError struct {
	sync.Once
}

func (l *logOneError) OnEvent(event Event) {
	switch event := event.(type) {
	case ErrorEvent:
		l.Once.Do(func() {
			log.Printf("LS Tracer error: (%s). NOTE: Set the SetGlobalEventHandler handler to log events.\n", event.Error())
		})
	}
}

// NewEventChannel returns an SetGlobalEventHandler callback handler, and a channel that
// produces the errors. When the channel buffer is full, subsequent errors will
// be dropped. A buffer size of less than one is incorrect, and will be adjusted
// to a buffer size of one.
func NewEventChannel(buffer int) (EventHandler, <-chan Event) {
	if buffer < 1 {
		buffer = 1
	}

	eventChan := make(chan Event, buffer)

	handler := func(event Event) {
		select {
		case eventChan <- event:
		default:
		}
	}

	return handler, eventChan
}
