package util

import (
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

type exitHandlerRegistry struct {
	handlers []func()
	mutex    sync.Mutex
}

var exitRegistry = exitHandlerRegistry{}

// RegisterExitHandler appends a func Exit handler to the list of handlers.
// The handlers will be invoked when vfkit receives a termination or interruption signal
//
// This method is useful when a caller wishes to execute a func before a shutdown.
func RegisterExitHandler(handler func()) {
	exitRegistry.mutex.Lock()
	defer exitRegistry.mutex.Unlock()
	exitRegistry.handlers = append(exitRegistry.handlers, handler)
}

// SetupExitSignalHandling sets up a signal channel to listen for termination or interruption signals.
// When one of these signals is received, all the registered exit handlers will be invoked, just
// before terminating the program.
func SetupExitSignalHandling() {
	setupExitSignalHandling(true)
}

// setupExitSignalHandling sets up a signal channel to listen for termination or interruption signals.
// When one of these signals is received, all the registered exit handlers will be invoked.
// It is possible to prevent the program from exiting by setting the doExit param to false (used for testing)
func setupExitSignalHandling(doExit bool) {
	sigChan := make(chan os.Signal, 2)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		for sig := range sigChan {
			log.Printf("captured %v, calling exit handlers and exiting..", sig)
			ExecuteExitHandlers()
			if doExit {
				os.Exit(1)
			}
		}
	}()
}

// ExecuteExitHandlers is call all registered exit handlers
// This function should be called when program finish work(i.e. when VM is turned off by guest OS)
func ExecuteExitHandlers() {
	exitRegistry.mutex.Lock()
	for _, handler := range exitRegistry.handlers {
		handler()
	}
	exitRegistry.mutex.Unlock()
}
