package io.trane.ndbc.pool;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

import io.trane.future.Future;
import io.trane.future.Promise;

public class PoolTest {

  @Test
  public void maxSize() {
    int maxSize = 100;
    Pool<TestItem> pool = Pool.apply(() -> Future.value(new TestItem()), maxSize, Integer.MAX_VALUE,
        Duration.ofMillis(Long.MAX_VALUE));
    AtomicInteger executing = new AtomicInteger();

    for (int i = 0; i < maxSize * 3; i++)
      pool.apply(t -> {
        executing.incrementAndGet();
        return Promise.apply();
      });

    assertEquals(maxSize, executing.get());
  }

  @Test
  public void maxSizeConcurrentCreation() {
    int maxSize = 100;
    Pool<TestItem> pool = Pool.apply(() -> Future.value(new TestItem()), maxSize, Integer.MAX_VALUE,
        Duration.ofMillis(Long.MAX_VALUE));
    AtomicInteger executing = new AtomicInteger();

    Concurrently.apply(Duration.ofMillis(200), () -> {
      pool.apply(t -> {
        executing.incrementAndGet();
        return Promise.apply();
      });
    }, () -> {
      assertTrue(maxSize >= executing.get());
    });
  }

  @Test
  public void maxSizeConcurrentUsage() {
    int maxSize = 100;
    Pool<TestItem> pool = Pool.apply(() -> Future.value(new TestItem()), maxSize, Integer.MAX_VALUE,
        Duration.ofMillis(Long.MAX_VALUE));
    AtomicInteger executing = new AtomicInteger();
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

    Concurrently.apply(Duration.ofMillis(200), () -> {
      pool.apply(t -> {
        executing.incrementAndGet();
        return Future.delay(Duration.ofMillis(1), scheduler).ensure(() -> executing.decrementAndGet());
      });
    }, () -> {
      assertTrue(maxSize >= executing.get());
    });
  }
  
  @Test
  public void maxWaiters() {
    int maxSize = 100;
    int maxWaiters = 60;
    Pool<TestItem> pool = Pool.apply(() -> Future.value(new TestItem()), maxSize, maxWaiters,
        Duration.ofMillis(Long.MAX_VALUE));
    AtomicInteger executing = new AtomicInteger();
    AtomicInteger rejected = new AtomicInteger();

    for (int i = 0; i < 200; i++) 
      pool.apply(t -> {
        executing.incrementAndGet();
        return Promise.apply();
      }).onFailure(e -> rejected.incrementAndGet());

    assertEquals(maxSize, executing.get());
    assertEquals(40, rejected.get());
  }
  
  @Test
  public void maxWaitersConcurrentCreation() {
    int maxSize = 100;
    Pool<TestItem> pool = Pool.apply(() -> Future.value(new TestItem()), maxSize, Integer.MAX_VALUE,
        Duration.ofMillis(Long.MAX_VALUE));
    AtomicInteger started = new AtomicInteger();
    AtomicInteger executing = new AtomicInteger();
    AtomicInteger rejected = new AtomicInteger();
    
    Concurrently.apply(Duration.ofMillis(200), () -> {
      started.incrementAndGet();
      pool.apply(t -> {
        executing.incrementAndGet();
        return Promise.apply();
      }).onFailure(e -> rejected.incrementAndGet());
    }, () -> {
      assertTrue(maxSize >= executing.get());
      System.out.println(executing.get());
    });
  }

}

class TestItem implements Pool.Item {
  @Override
  public Future<Void> release() {
    return Future.VOID;
  }

  @Override
  public Future<Boolean> validate() {
    return Future.TRUE;
  }
}
@@ -457,32 +457,31 @@
 0;%0A    Pool%3C
-TestItem
+Integer
 %3E pool = Poo
@@ -503,39 +503,62 @@
 uture.value(
-new TestItem())
+1), i -%3E Future.VOID, i -%3E Future.TRUE
 , maxSize, I
@@ -547,32 +547,40 @@
 e.TRUE, maxSize,
+%0A       
  Integer.MAX_VAL
@@ -578,32 +578,24 @@
 r.MAX_VALUE,
-%0A       
  Duration.of
@@ -946,32 +946,31 @@
 0;%0A    Pool%3C
-TestItem
+Integer
 %3E pool = Poo
@@ -992,39 +992,62 @@
 uture.value(
-new TestItem())
+1), i -%3E Future.VOID, i -%3E Future.TRUE
 , maxSize, I
@@ -1036,32 +1036,40 @@
 e.TRUE, maxSize,
+%0A       
  Integer.MAX_VAL
@@ -1067,32 +1067,24 @@
 r.MAX_VALUE,
-%0A       
  Duration.of
@@ -1469,32 +1469,31 @@
 0;%0A    Pool%3C
-TestItem
+Integer
 %3E pool = Poo
@@ -1515,39 +1515,62 @@
 uture.value(
-new TestItem())
+1), i -%3E Future.VOID, i -%3E Future.TRUE
 , maxSize, I
@@ -1559,32 +1559,40 @@
 e.TRUE, maxSize,
+%0A       
  Integer.MAX_VAL
@@ -1590,32 +1590,24 @@
 r.MAX_VALUE,
-%0A       
  Duration.of
@@ -2060,27 +2060,25 @@
     %7D);%0A  %7D%0A
-  
 %0A
+
   @Test%0A  pu
@@ -2155,32 +2155,31 @@
 0;%0A    Pool%3C
-TestItem
+Integer
 %3E pool = Poo
@@ -2201,39 +2201,62 @@
 uture.value(
-new TestItem())
+1), i -%3E Future.VOID, i -%3E Future.TRUE
 , maxSize, m
@@ -2445,17 +2445,16 @@
 00; i++)
- 
 %0A      p
@@ -2679,19 +2679,17 @@
 ));%0A  %7D%0A
-  
 %0A
+
   @Test%0A
@@ -2750,32 +2750,57 @@
  maxSize = 100;%0A
+    int maxWaiters = 60;%0A
     Pool%3CTestIte
@@ -2792,24 +2792,23 @@
    Pool%3C
-TestItem
+Integer
 %3E pool =
@@ -2842,51 +2842,67 @@
 lue(
-new TestItem()), maxSize, Integer.MAX_VALUE
+1), i -%3E Future.VOID, i -%3E Future.TRUE, maxSize, maxWaiters
 ,%0A  
@@ -3097,13 +3097,9 @@
 ();%0A
-    
 %0A
+
     
@@ -3450,198 +3450,4 @@
 %7D%0A%0A%7D
-%0A%0Aclass TestItem implements Pool.Item %7B%0A  @Override%0A  public Future%3CVoid%3E release() %7B%0A    return Future.VOID;%0A  %7D%0A%0A  @Override%0A  public Future%3CBoolean%3E validate() %7B%0A    return Future.TRUE;%0A  %7D%0A%7D

fix pool tests