package com.qmth.boot.redis.concurrent;

import com.qmth.boot.core.concurrent.model.Semaphore;
import com.qmth.boot.core.concurrent.service.ConcurrentService;
import com.qmth.boot.redis.constant.RedisConstant;
import com.qmth.boot.redis.rateLimit.RedisRateLimitService;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

@Component("concurrentService")
public class RedisConcurrentService implements ConcurrentService, RedisConstant {

    private static final Logger log = LoggerFactory.getLogger(RedisRateLimitService.class);

    private RedissonClient redissonClient;

    public RedisConcurrentService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
        log.info("Redis concurrent service inited.");
    }

    private String getLockKey(String name) {
        return LOCK_PREFIX.concat(KEY_SEPARATOR).concat(name);
    }

    private String getReadWriteLockKey(String name) {
        return READ_WRITE_LOCK_PREFIX.concat(KEY_SEPARATOR).concat(name);
    }

    @Override
    public Lock getLock(String name) {
        return redissonClient.getLock(getLockKey(name));
    }

    @Override
    public boolean isLocked(String name) {
        return redissonClient.getLock(getLockKey(name)).isLocked();
    }

    @Override
    public ReadWriteLock getReadWriteLock(String name) {
        return redissonClient.getReadWriteLock(getReadWriteLockKey(name));
    }

    @Override
    public boolean isReadLocked(String name) {
        return redissonClient.getReadWriteLock(getReadWriteLockKey(name)).readLock().isLocked();
    }

    @Override
    public boolean isWriteLocked(String name) {
        return redissonClient.getReadWriteLock(getReadWriteLockKey(name)).writeLock().isLocked();
    }

    @Override
    public Semaphore getSemaphore(@NotNull String name) {
        RSemaphore semaphore = redissonClient.getSemaphore(name);
        semaphore.trySetPermits(1);
        return new Semaphore() {

            @Override
            public void acquire() throws InterruptedException {
                semaphore.acquire();
            }

            @Override
            public boolean tryAcquire() {
                return semaphore.tryAcquire();
            }

            @Override
            public void release() {
                semaphore.release();
            }

            @Override
            public boolean isAvailable() {
                return semaphore.availablePermits() > 0;
            }
        };
    }

}
