package com.ssyf.common.core.util;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 简单锁工具
 * @author huwei
 * @date 2022/11/13 9:20
 */
public class SimpleLockerUtil {

    private static final Map<String,LockerInfo> lockerMap = new HashMap<>();

    private static final Lock locker = new ReentrantLock();

    /**
     * 获取锁对象
     * @param key
     * @return
     */
    public static Lock getLocker(String key){
        locker.lock();
        try{
            LockerInfo lockerInfo = lockerMap.get(key);
            if(lockerInfo == null){
                lockerInfo = new LockerInfo(key);
                lockerMap.put(key ,lockerInfo);
            }else{
                lockerInfo.increReference();
            }
            return lockerInfo.getLocker();
        }finally {
            locker.unlock();
        }
    }

    /**
     * 获取锁对象
     * @param key
     * @param domains 域，防止相同key获取同一锁对象造成的同步问题
     * @return
     */
    public static Lock getLocker(String key , String... domains){
        return getLocker(wrapKey(key, domains));
    }

    public static int getReferenceCount(String key){
        LockerInfo lockInfo = lockerMap.get(key);
        if(lockInfo != null){
            return lockInfo.getReferenceCount();
        }
        return 0;
    }

    /**
     * 释放锁对象
     * @param key
     */
    private static void releaseLocker(String key){
        locker.lock();
        try{
            LockerInfo lockerInfo = lockerMap.get(key);
            if(lockerInfo != null){
                lockerInfo.decreReference();
                if(lockerInfo.getReferenceCount().intValue() <= 0){
                    lockerMap.remove(key);
                }
            }
        }finally {
            locker.unlock();
        }
    }

    private static String wrapKey(String key , String... domains) {
        StringBuffer stringBuilder = new StringBuffer();
        for(String domain : domains){
            stringBuilder.append(domain);
        }
        return stringBuilder.append(key).toString();
    }

    /**
     * 自动引用锁类
     * @author huwei
     * @date 2022/11/13 10:18
     */
    public static class AutoReferenceLocker extends ReentrantLock {
        private String key;

        AutoReferenceLocker(String key){
            this.key = key;
        }

        @Override
        public void unlock() {
            super.unlock();

            //释放
            releaseLocker(key);
        }
        public int getReferenceCount(){
            return SimpleLockerUtil.getReferenceCount(key);
        }
    }


    /**
     * 锁信息类
     * @author huwei
     * @date 2022/11/13 10:18
     */
    @Data
    static class LockerInfo {
        ReentrantLock locker;

        Integer referenceCount;

        LockerInfo(String key){
            this.locker = new AutoReferenceLocker(key);
            this.referenceCount = 1;
        }

        void increReference(){
            this.referenceCount++;
        }

        void decreReference(){
            this.referenceCount--;
        }
    }
}
