package locker

import (
	"sync"
)

type Elem struct {
	Pipe  chan bool
	Usage int
}

func NewElem() *Elem {
	return &Elem{
		Pipe: make(chan bool, 1),
	}
}

type Locker struct {
	mtx  sync.Mutex
	lMap map[string]*Elem
}

func NewLocker() *Locker {
	lock := &Locker{
		lMap: make(map[string]*Elem),
	}
	return lock
}

func (lock *Locker) WaitAndLock(name string) {
	lock.mtx.Lock()
	p, exist := lock.lMap[name]
	if !exist {
		p = NewElem()
		lock.lMap[name] = p
		p.Pipe <- true
	}
	p.Usage += 1
	lock.mtx.Unlock()
	select {
	case <-p.Pipe:
		// NOP
	}
}

func (lock *Locker) Done(name string) {
	lock.mtx.Lock()
	p, exist := lock.lMap[name]
	if exist {
		p.Pipe <- true
		if p.Usage > 0 {
			p.Usage -= 1
		}
	}
	garbageKeys := make([]string, 0)
	for key, _ := range lock.lMap {
		elem := lock.lMap[key]
		if elem.Usage == 0 && key != name {
			garbageKeys = append(garbageKeys, key)
		}
	}
	for _, key := range garbageKeys {
		delete(lock.lMap, key)
	}
	lock.mtx.Unlock()
}
