package cn.com.qmth.stmms.biz.mark.service.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 与评卷相关的全局状态锁<br>
 * 暂时只实现JVM内存锁，后续加上Redis实现的集群锁
 * 
 * @author luoshi
 *
 */
@Component
public class MarkLockService {

    protected static final Logger log = LoggerFactory.getLogger(MarkLockService.class);

    private Map<Object, AtomicBoolean> groupMap = new HashMap<>();

    private Map<Object, AtomicBoolean> studentMap = new HashMap<>();

    private Map<Object, AtomicBoolean> markerMap = new HashMap<>();

    /**
     * 对评卷分组进行重置/删除操作前，先尝试锁定
     * 
     * @param group
     */
    public void lockGroup(Integer examId, String subjectCode, Integer groupNumber) {
        AtomicBoolean lock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
        while (!lock.get()) {
            lock.compareAndSet(false, true);
        }
    }

    /**
     * 对评卷分组进行重置/删除操作后，释放锁定
     * 
     * @param group
     */
    public void unlockGroup(Integer examId, String subjectCode, Integer groupNumber) {
        AtomicBoolean lock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
        while (lock != null && lock.get()) {
            lock.compareAndSet(true, false);
        }
    }

    /**
     * 等待评卷分组释放锁定
     * 
     * @param group
     */
    public void waitUnlockGroup(Integer examId, String subjectCode, Integer groupNumber) {
        AtomicBoolean lock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
        while (lock.get()) {
            ;
        }
    }

    /**
     * 对考生评卷任务进行删除操作前，先尝试锁定
     * 
     * @param studentId
     */
    public void lockStudent(Integer studentId) {
        AtomicBoolean lock = getLock(studentMap, studentId);
        while (!lock.get()) {
            lock.compareAndSet(false, true);
        }
    }

    /**
     * 对考生评卷任务进行删除操作后，释放锁定
     * 
     * @param studentId
     */
    public void unlockStudent(Integer studentId) {
        AtomicBoolean lock = getLock(studentMap, studentId);
        while (lock.get()) {
            lock.compareAndSet(true, false);
        }
    }

    /**
     * 等待考生释放锁定
     * 
     * @param studentId
     */
    public void waitUnlockStudent(Integer studentId) {
        AtomicBoolean lock = getLock(studentMap, studentId);
        while (lock.get()) {
            ;
        }
    }

    /**
     * 评卷员进行评卷操作前，先尝试锁定
     * 
     * @param markerId
     */
    public void lockMarker(Integer markerId) {
        AtomicBoolean lock = getLock(markerMap, markerId);
        while (!lock.get()) {
            lock.compareAndSet(false, true);
        }
    }

    /**
     * 评卷员进行评卷操作后，释放锁定
     * 
     * @param markerId
     */
    public void unlockMarker(Integer markerId) {
        AtomicBoolean lock = getLock(markerMap, markerId);
        while (lock.get()) {
            lock.compareAndSet(true, false);
        }
    }

    /**
     * 等待评卷员释放锁定
     * 
     * @param studentId
     */
    public void waitUnlockMarker(Integer markerId) {
        AtomicBoolean lock = getLock(markerMap, markerId);
        while (lock.get()) {
            ;
        }
    }

    public void clear() {
        clearLock(groupMap);
        clearLock(studentMap);
        clearLock(markerMap);
    }

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

    private AtomicBoolean getLock(Map<Object, AtomicBoolean> map, Object key) {
        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;
    }

    private String getKey(Integer examId, String subjectCode, Integer groupNumber) {
        return examId + "_" + subjectCode + "_" + groupNumber;
    }

}
