package fr.umlv.tpnote2;

import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public interface Pool {
  @FunctionalInterface
  public interface Promise {
    public void register(Consumer consumer);
    
    /*public default V waitIfNotAvailableAndGet() throws InterruptedException {
      class Listener implements Consumer {
        @Override
        public void accept(V value) {
          //TODO
        }
        
        public V waitIfNotAvailableAndGet() throws InterruptedException {
          return null; //TODO
        }
      }
      Listener listener = new Listener();
      register(listener);
      return listener.waitIfNotAvailableAndGet();
    }*/
    
    /*public default Promise filter(Predicate<V> predicate) {
      return null; // TODO
    }
    
    public default <U> Promise map(Function<V, U> mapper) {
      return null; // TODO
    }*/
  }
  
  public <V> Promise execute(Supplier supplier);
  
  // public void close();
  
  static class Result implements Promise, Runnable {
    private Object value;
    private final Supplier supplier;
    
    Result(Supplier supplier) {
      this.supplier = supplier;
    }
    
    @Override
    public void register(Consumer consumer) {
      if (value != null) {
        consumer.accept(value);  
      } else {
        // TODO
      }
    }
    @Override
    public void run() {
      Object result = Objects.requireNonNull(supplier.get());
      
      value = result;
      //TODO
    }
  }
  
  public static Pool newSimplePool() {
    return new Pool() {
      @Override
      public <V> Promise execute(Supplier supplier) {
        Result result = new Result(supplier);
        // TODO start a thread that calls method the method Result.run()
        return result;
      }
    };
  }
  
  public static Pool newFixedSizePool(int numberOfThreads, int queueSize) {
    return new Pool() {
      private final Result POISON = new Result(null);  // used to stop the thread (when closed)
      //TODO add a producer/consumer queue here
      
      {
        // TODO starts numberOfThreads threads here
      }
        
      @Override
      public <V> Promise execute(Supplier supplier) {
        Result result = new Result(supplier);
        // add 'result' to the producer/consumer queue here
        return result;
      }
    };
  }
}
