package cn.com.qmth.stmms.biz.lock.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.springframework.stereotype.Component;

import cn.com.qmth.stmms.biz.lock.LockProvider;
import cn.com.qmth.stmms.common.enums.LockType;

/**
 * JVM内存实现的锁控制
 * 
 * @author luoshi
 *
 */
@Component("memoryLockProvider")
public class MemoryLockProvider implements LockProvider {

    private Map<LockType, Map<String, AtomicBoolean>> lockMap = new HashMap<>();

    @Override
    public void waitLock(LockType type, String key) {
        AtomicBoolean lock = getLock(type, key);
        while (!lock.get()) {
            lock.compareAndSet(false, true);
        }
    }

    @Override
    public boolean tryLock(LockType type, String key) {
        AtomicBoolean lock = getLock(type, key);
        return lock.compareAndSet(false, true);
    }

    @Override
    public boolean isLocked(LockType type, String key) {
        AtomicBoolean lock = getLock(type, key);
        return lock.get();
    }

    @Override
    public void unlock(LockType type, String key) {
        AtomicBoolean lock = getLock(type, key);
        while (lock.get()) {
            lock.compareAndSet(true, false);
        }
    }

    @Override
    public void waitUnlock(LockType type, String key) {
        AtomicBoolean lock = getLock(type, key);
        while (lock.get()) {
            ;
        }
    }

    private AtomicBoolean getLock(LockType type, String key) {
        Map<String, AtomicBoolean> map = lockMap.get(type);
        if (map == null) {
            synchronized (lockMap) {
                map = lockMap.get(type);
                if (map == null) {
                    map = new HashMap<>();
                    lockMap.put(type, map);
                }
            }
        }

        AtomicBoolean lock = map.get(key);
        if (lock == null) {
            synchronized (map) {
                lock = map.get(key);
                if (lock == null) {
                    lock = new AtomicBoolean(false);
                    map.put(key, lock);
                }
            }
        }

        return lock;
    }

    public void clear() {
        if (lockMap.isEmpty()) {
            return;
        }
        synchronized (lockMap) {
            for (Map<String, AtomicBoolean> map : lockMap.values()) {
                Set<String> keys = new HashSet<>();
                for (Entry<String, AtomicBoolean> entry : map.entrySet()) {
                    if (!entry.getValue().get()) {
                        keys.add(entry.getKey());
                    }
                }
                for (String key : keys) {
                    map.remove(key);
                }
                keys.clear();
            }
        }
    }

}
