package ctxwatch

import (
	
	
)

// ContextWatcher watches a context and performs an action when the context is canceled. It can watch one context at a
// time.
type ContextWatcher struct {
	handler Handler

	// Lock protects the members below.
	lock sync.Mutex
	// Stop is the handle for an "after func". See [context.AfterFunc].
	stop func() bool
	done chan struct{}
}

// NewContextWatcher returns a ContextWatcher. onCancel will be called when a watched context is canceled.
// OnUnwatchAfterCancel will be called when Unwatch is called and the watched context had already been canceled and
// onCancel called.
func ( Handler) *ContextWatcher {
	 := &ContextWatcher{
		handler: ,
	}

	return 
}

// Watch starts watching ctx. If ctx is canceled then the onCancel function passed to NewContextWatcher will be called.
func ( *ContextWatcher) ( context.Context) {
	.lock.Lock()
	defer .lock.Unlock()

	if .stop != nil {
		panic("watch already in progress")
	}

	if .Done() != nil {
		.done = make(chan struct{})
		.stop = context.AfterFunc(, func() {
			.handler.HandleCancel()
			close(.done)
		})
	}
}

// Unwatch stops watching the previously watched context. If the onCancel function passed to NewContextWatcher was
// called then onUnwatchAfterCancel will also be called.
func ( *ContextWatcher) () {
	.lock.Lock()
	defer .lock.Unlock()

	if .stop != nil {
		if !.stop() {
			<-.done
			.handler.HandleUnwatchAfterCancel()
		}
		.stop = nil
		.done = nil
	}
}

type Handler interface {
	// HandleCancel is called when the context that a ContextWatcher is currently watching is canceled. canceledCtx is the
	// context that was canceled.
	HandleCancel(canceledCtx context.Context)

	// HandleUnwatchAfterCancel is called when a ContextWatcher that called HandleCancel on this Handler is unwatched.
	HandleUnwatchAfterCancel()
}