package com.qmth.boot.core.concurrent.service.impl;

import com.qmth.boot.core.concurrent.model.Semaphore;
import com.qmth.boot.core.concurrent.service.ConcurrentService;

import javax.annotation.PreDestroy;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MemoryConcurrentService implements ConcurrentService {

    private final Map<String, ReentrantLock> lockMap;

    private final Map<String, ReentrantReadWriteLock> readWriteMap;

    private final Map<String, Semaphore> semaphoreMap;

    public MemoryConcurrentService() {
        this.lockMap = new HashMap<>();
        this.readWriteMap = new HashMap<>();
        this.semaphoreMap = new HashMap<>();
    }

    @PreDestroy
    public void close() {
        this.lockMap.clear();
        this.readWriteMap.clear();
        this.semaphoreMap.clear();
    }

    private ReentrantLock lock(String name) {
        ReentrantLock lock = lockMap.get(name);
        if (lock == null) {
            synchronized (lockMap) {
                lock = lockMap.computeIfAbsent(name, key -> new ReentrantLock());
            }
        }
        return lock;
    }

    private ReentrantReadWriteLock readWriteLock(String name) {
        ReentrantReadWriteLock lock = readWriteMap.get(name);
        if (lock == null) {
            synchronized (readWriteMap) {
                lock = readWriteMap.computeIfAbsent(name, key -> new ReentrantReadWriteLock());
            }
        }
        return lock;
    }

    @Override
    public Lock getLock(String name) {
        return lock(name);
    }

    @Override
    public boolean isLocked(String name) {
        return lock(name).isLocked();
    }

    @Override
    public ReadWriteLock getReadWriteLock(String name) {
        return readWriteLock(name);
    }

    @Override
    public boolean isReadLocked(String name) {
        return readWriteLock(name).getReadLockCount() > 0;
    }

    @Override
    public boolean isWriteLocked(String name) {
        return readWriteLock(name).isWriteLocked();
    }

    @Override
    public Semaphore getSemaphore(@NotNull String name) {
        Semaphore semaphore = semaphoreMap.get(name);
        if (semaphore == null) {
            synchronized (semaphoreMap) {
                semaphore = semaphoreMap.computeIfAbsent(name, key -> {
                    java.util.concurrent.Semaphore se = new java.util.concurrent.Semaphore(1, true);
                    return new Semaphore() {

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

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

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

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