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.locks.ReentrantReadWriteLock;

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("jdkLockProvider")
public class JdkLockProvider implements LockProvider {

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

    @Override
    public void waitLock(LockType type, String key) {
        getLock(type, key).writeLock().lock();
    }

    @Override
    public boolean tryLock(LockType type, String key) {
        return getLock(type, key).writeLock().tryLock();
    }

    @Override
    public boolean isLocked(LockType type, String key) {
        return getLock(type, key).isWriteLocked();
    }

    @Override
    public void unlock(LockType type, String key) {
        getLock(type, key).writeLock().unlock();
    }

    @Override
    public void watch(LockType type, String key) {
        getLock(type, key).readLock().lock();
    }

    @Override
    public void unwatch(LockType type, String key) {
        getLock(type, key).readLock().unlock();
    }

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

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

        return lock;
    }

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

}
