// Package breaker provides OSSignals struct for OS signals handling (with context).
package breaker

import (
	"context"
	"os"
	"os/signal"
	"syscall"
)

// OSSignals allows subscribing for system signals.
type OSSignals struct {
	ctx context.Context
	ch  chan os.Signal
}

// NewOSSignals creates new subscriber for system signals.
func NewOSSignals(ctx context.Context) OSSignals {
	return OSSignals{
		ctx: ctx,
		ch:  make(chan os.Signal, 1),
	}
}

// Subscribe for some system signals (call Stop for stopping).
func (oss *OSSignals) Subscribe(onSignal func(os.Signal), signals ...os.Signal) {
	if len(signals) == 0 {
		signals = []os.Signal{os.Interrupt, syscall.SIGINT, syscall.SIGTERM} // default signals
	}

	signal.Notify(oss.ch, signals...)

	go func(ch <-chan os.Signal) {
		select {
		case <-oss.ctx.Done():
			break

		case sig, opened := <-ch:
			if oss.ctx.Err() != nil {
				break
			}

			if opened && sig != nil {
				onSignal(sig)
			}
		}
	}(oss.ch)
}

// Stop system signals listening.
func (oss *OSSignals) Stop() {
	signal.Stop(oss.ch)
	close(oss.ch)
}