package utils

import (
	"io"
	"sync"

	list "github.com/bahlo/generic-list-go"
)

type Callback[T any] struct {
	list  list.List[func(T)]
	mutex sync.RWMutex
}

func NewCallback[T any]() *Callback[T] {
	return &Callback[T]{}
}

func (c *Callback[T]) Register(item func(T)) io.Closer {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	element := c.list.PushBack(item)
	return &callbackCloser[T]{
		element:  element,
		callback: c,
	}
}

func (c *Callback[T]) Emit(item T) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	for element := c.list.Front(); element != nil; element = element.Next() {
		go element.Value(item)
	}
}

type callbackCloser[T any] struct {
	element  *list.Element[func(T)]
	callback *Callback[T]
	once     sync.Once
}

func (c *callbackCloser[T]) Close() error {
	c.once.Do(func() {
		c.callback.mutex.Lock()
		defer c.callback.mutex.Unlock()
		c.callback.list.Remove(c.element)
	})
	return nil
}