package dataext

import "sync"

type SyncMap[TKey comparable, TData any] struct {
	data map[TKey]TData
	lock sync.Mutex
}

func (s *SyncMap[TKey, TData]) Set(key TKey, data TData) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	s.data[key] = data
}

func (s *SyncMap[TKey, TData]) SetIfNotContains(key TKey, data TData) bool {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	if _, existsInPreState := s.data[key]; existsInPreState {
		return false
	}

	s.data[key] = data

	return true
}

func (s *SyncMap[TKey, TData]) SetIfNotContainsFunc(key TKey, data func() TData) bool {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	if _, existsInPreState := s.data[key]; existsInPreState {
		return false
	}

	s.data[key] = data()

	return true
}

func (s *SyncMap[TKey, TData]) Get(key TKey) (TData, bool) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	if v, ok := s.data[key]; ok {
		return v, true
	} else {
		return *new(TData), false
	}
}

func (s *SyncMap[TKey, TData]) GetAndSetIfNotContains(key TKey, data TData) TData {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	if v, ok := s.data[key]; ok {
		return v
	} else {
		s.data[key] = data
		return data
	}
}

func (s *SyncMap[TKey, TData]) GetAndSetIfNotContainsFunc(key TKey, data func() TData) TData {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	if v, ok := s.data[key]; ok {
		return v
	} else {
		dataObj := data()
		s.data[key] = dataObj
		return dataObj
	}
}

func (s *SyncMap[TKey, TData]) Delete(key TKey) bool {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	_, ok := s.data[key]

	delete(s.data, key)

	return ok
}

func (s *SyncMap[TKey, TData]) Contains(key TKey) bool {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	_, ok := s.data[key]

	return ok
}

func (s *SyncMap[TKey, TData]) GetAllKeys() []TKey {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	r := make([]TKey, 0, len(s.data))

	for k := range s.data {
		r = append(r, k)
	}

	return r
}

func (s *SyncMap[TKey, TData]) GetAllValues() []TData {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.data == nil {
		s.data = make(map[TKey]TData)
	}

	r := make([]TData, 0, len(s.data))

	for _, v := range s.data {
		r = append(r, v)
	}

	return r
}