/*
 * Copyright 2014 Timothy Brooks
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.uncontended.precipice.pattern;

import net.uncontended.precipice.Controller;
import net.uncontended.precipice.Rejected;
import net.uncontended.precipice.RejectedException;
import net.uncontended.precipice.Result;
import net.uncontended.precipice.concurrent.PrecipicePromise;

import java.util.List;

public class Balancer<T extends Enum<T> & Result> {

    private final Controller<T> controller;
    private final List<Controller<T>> pool;

    public Balancer(Controller<T> controller, List<Controller<T>> pool) {
        this.controller = controller;
        this.pool = pool;
    }

    public <R> PatternPair<T, R> next() {
        PrecipicePromise<T, R> promise = controller.acquirePermitAndGetPromise();
        Controller<T> controller = pool.get(0);
        Rejected rejected = controller.acquirePermitOrGetRejectedReason();
        if (rejected != null) {
            throw new RejectedException(rejected);
        }
        // TODO: Double:
        long startTime = System.nanoTime();
        return new PatternPair<>(controller, startTime, promise);
    }

    private static class PatternPair<T extends Enum<T> & Result, R> {
        private final Controller<T> controller;
        private final long startTime;
        private final PrecipicePromise<T, R> parent;

        private PatternPair(Controller<T> controller, long startTime, PrecipicePromise<T, R> parent) {
            this.controller = controller;
            this.startTime = startTime;
            this.parent = parent;
        }

        public PrecipicePromise<T, R> promise() {
            return controller.getPromise(startTime, parent);
        }
    }
}

@@ -1244,77 +1244,341 @@
     
-PrecipicePromise%3CT, R%3E promise = controller.acquirePermitAndGetPromis
+Rejected rejected = controller.acquirePermitOrGetRejectedReason();%0A        if (rejected != null) %7B%0A            long nanoTime = System.nanoTime();%0A            controller.getActionMetrics().incrementRejectionCount(rejected, nanoTime);%0A            throw new RejectedException(rejected);%0A        %7D%0A%0A        long nanoTime = System.nanoTim
 e();
@@ -1593,32 +1593,37 @@
  Controller%3CT%3E c
+hildC
 ontroller = pool
@@ -1622,127 +1622,231 @@
 r = 
-pool.get(0);%0A        Rejected rejected = controller.acquirePermitOrGetRejectedReason();%0A        if (rejected != null) %7B
+findNext(nanoTime);%0A        if (childController == null) %7B%0A            controller.getActionMetrics().incrementRejectionCount(Rejected.ALL_SERVICES_REJECTED, nanoTime);%0A            controller.getSemaphore().releasePermit(1);
 %0A   
@@ -1886,94 +1886,131 @@
 ion(
-r
+R
 ejected
-);%0A        %7D%0A        // TODO: Double:%0A        long startTime = System.
+.ALL_SERVICES_REJECTED);%0A        %7D%0A        %0A        PrecipicePromise%3CT, R%3E promise = controller.getPromise(
 nanoTime
 ();%0A
@@ -1997,33 +1997,32 @@
 Promise(nanoTime
-(
 );%0A        retur
@@ -2057,27 +2057,429 @@
 er, 
-startTime, promise)
+controller.getPromise(nanoTime, promise));%0A    %7D%0A%0A    private Controller%3CT%3E findNext(long nanoTime) %7B%0A        Controller%3CT%3E controller = pool.get(0);%0A        Rejected rejected = controller.acquirePermitOrGetRejectedReason();%0A        if (rejected != null) %7B%0A            controller.getActionMetrics().incrementRejectionCount(rejected, nanoTime);%0A        %7D else %7B%0A            return controller;%0A        %7D%0A        return null
 ;%0A  
@@ -2605,46 +2605,8 @@
 er;%0A
-        private final long startTime;%0A
     
@@ -2647,21 +2647,22 @@
 %3CT, R%3E p
-arent
+romise
 ;%0A%0A     
@@ -2713,24 +2713,8 @@
 ler,
- long startTime,
  Pre
@@ -2734,21 +2734,22 @@
 %3CT, R%3E p
-arent
+romise
 ) %7B%0A    
@@ -2807,185 +2807,25 @@
 his.
-startTime = startTime;%0A            this.parent = parent;%0A        %7D%0A%0A        public PrecipicePromise%3CT, R%3E promise() %7B%0A            return controller.getPromise(startTime, parent)
+promise = promise
 ;%0A  

Implement new pattern idea