package org.redisson;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Assume;

import org.junit.Test;
import org.redisson.core.RSemaphore;

public class RedissonSemaphoreTest extends BaseConcurrentTest {

    @Test
    public void testBlockingAcquire() throws InterruptedException {
        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(1);
        s.acquire();

        Thread t = new Thread() {
            @Override
            public void run() {
                RSemaphore s = redisson.getSemaphore("test");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                s.release();
            }
        };

        t.start();

        assertThat(s.availablePermits()).isEqualTo(0);
        s.acquire();
        assertThat(s.tryAcquire()).isFalse();
        assertThat(s.availablePermits()).isEqualTo(0);
    }

    @Test
    public void testBlockingNAcquire() throws InterruptedException {
        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(5);
        s.acquire(3);

        Thread t = new Thread() {
            @Override
            public void run() {
                RSemaphore s = redisson.getSemaphore("test");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                s.release();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                s.release();
            }
        };

        assertThat(s.availablePermits()).isEqualTo(2);
        t.start();

        s.acquire(4);
        assertThat(s.availablePermits()).isEqualTo(0);
    }

    @Test
    public void testTryNAcquire() throws InterruptedException {
        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(5);
        assertThat(s.tryAcquire(3)).isTrue();

        Thread t = new Thread() {
            @Override
            public void run() {
                RSemaphore s = redisson.getSemaphore("test");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                s.release();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                s.release();
            }
        };

        assertThat(s.tryAcquire(4)).isFalse();

        t.start();
        t.join(1);

        long startTime = System.currentTimeMillis();
        assertThat(s.tryAcquire(4, 1, TimeUnit.SECONDS)).isTrue();
        assertThat(System.currentTimeMillis() - startTime).isBetween(900L, 1020L);
        assertThat(s.availablePermits()).isEqualTo(0);
    }

    @Test
    public void testReleaseWithoutPermits() {
        RSemaphore s = redisson.getSemaphore("test");
        s.release();

        assertThat(s.availablePermits()).isEqualTo(1);
    }

    @Test
    public void testDrainPermits() throws InterruptedException {
        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(10);
        s.acquire(3);

        assertThat(s.drainPermits()).isEqualTo(7);
        assertThat(s.availablePermits()).isEqualTo(0);
    }

    @Test
    public void testReleaseAcquire() throws InterruptedException {
        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(10);
        s.acquire();
        assertThat(s.availablePermits()).isEqualTo(9);
        s.release();
        assertThat(s.availablePermits()).isEqualTo(10);
        s.acquire(5);
        assertThat(s.availablePermits()).isEqualTo(5);
        s.release(5);
        assertThat(s.availablePermits()).isEqualTo(10);
    }


    @Test
    public void testConcurrency_SingleInstance() throws InterruptedException {
        final AtomicInteger lockedCounter = new AtomicInteger();

        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(1);

        int iterations = 15;
        testSingleInstanceConcurrency(iterations, r -> {
            RSemaphore s1 = r.getSemaphore("test");
            try {
                s1.acquire();
            }catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            int value = lockedCounter.get();
            lockedCounter.set(value + 1);
            s1.release();
        });

        assertThat(lockedCounter.get()).isEqualTo(iterations);
    }

    @Test
    public void testConcurrencyLoop_MultiInstance() throws InterruptedException {
        final int iterations = 100;
        final AtomicInteger lockedCounter = new AtomicInteger();

        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(1);

        testMultiInstanceConcurrency(16, r -> {
            for (int i = 0; i < iterations; i++) {
                try {
                    r.getSemaphore("test").acquire();
                }catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int value = lockedCounter.get();
                lockedCounter.set(value + 1);
                r.getSemaphore("test").release();
            }
        });

        assertThat(lockedCounter.get()).isEqualTo(16 * iterations);
    }

    @Test
    public void testConcurrency_MultiInstance_1_permits() throws InterruptedException {
        int iterations = 30;
        final AtomicInteger lockedCounter = new AtomicInteger();

        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(1);

        testMultiInstanceConcurrency(iterations, r -> {
            RSemaphore s1 = r.getSemaphore("test");
            try {
                s1.acquire();
            }catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            int value = lockedCounter.get();
            lockedCounter.set(value + 1);
            s1.release();
        });

        assertThat(lockedCounter.get()).isEqualTo(iterations);
    }

    @Test
    public void testConcurrency_MultiInstance_10_permits() throws InterruptedException {
        int iterations = 105;
        Assume.assumeFalse(Boolean.valueOf(System.getProperty("travisEnv")));
        final AtomicInteger lockedCounter = new AtomicInteger();

        RSemaphore s = redisson.getSemaphore("test");
        s.setPermits(10);

        final AtomicInteger checkPermits = new AtomicInteger(s.availablePermits());
        final CyclicBarrier barrier = new CyclicBarrier(s.availablePermits());
        testMultiInstanceConcurrencySequentiallyLaunched(iterations, r -> {
            RSemaphore s1 = r.getSemaphore("test");
            try {
                s1.acquire();
                barrier.await();
                if (checkPermits.decrementAndGet() > 0) {
                    assertThat(s1.availablePermits()).isEqualTo(0);
                    assertThat(s1.tryAcquire()).isFalse();
                } else {
                    Thread.sleep(50);
                }
            }catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }catch (BrokenBarrierException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            int value = lockedCounter.get();
            lockedCounter.set(value + 1);
            s1.release();
        });

        System.out.println(lockedCounter.get());
        
        assertThat(lockedCounter.get()).isLessThan(iterations);
    }

}

@@ -7211,38 +7211,8 @@
 n %7B%0A
-        int iterations = 105;%0A
     
@@ -7281,24 +7281,54 @@
 visEnv%22)));%0A
+        int iterations = 100;%0A
         fina

revert back iteration to 100