package src

import "sync"

type CMap[K comparable, V any] struct {
	data map[K]V
	lock sync.RWMutex
}

func NewCMap[K comparable, V any]() CMap[K, V] {
	return CMap[K, V]{
		data: make(map[K]V),
	}
}

func (m *CMap[K, V]) Get(key K) (V, bool) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	ret, ok := m.data[key]
	return ret, ok
}

func (m *CMap[K, V]) GetOrCreate(key K, create func() V) (V, bool) {
	m.lock.RLock()
	ret, ok := m.data[key]
	if ok {
		m.lock.RUnlock()
		return ret, false
	}
	m.lock.RUnlock()

	// data does not exist, create it
	m.lock.Lock()
	defer m.lock.Unlock()

	// check if another gorountine already created it before we could lock
	ret, ok = m.data[key]
	if ok {
		return ret, false
	}

	val := create()
	m.data[key] = val
	return val, true
}

func (m *CMap[K, V]) GetOrSet(key K, val V) (V, bool) {
	return m.GetOrCreate(key, func() V { return val })
}

func (m *CMap[K, V]) Set(key K, val V) {
	m.lock.Lock()
	defer m.lock.Unlock()

	m.data[key] = val
}

func (m *CMap[K, V]) Remove(key K) {
	m.lock.Lock()
	defer m.lock.Unlock()

	delete(m.data, key)
}

func (m *CMap[K, V]) GetAndRemove(key K) (V, bool) {
	m.lock.Lock()
	defer m.lock.Unlock()

	val, ok := m.data[key]
	delete(m.data, key)
	return val, ok
}
