/*
 * Copyright 2012-2014 Brandon Beck
 *
 * 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 org.isomorphism.util;

import com.google.common.base.Ticker;
import com.google.common.util.concurrent.Uninterruptibles;

import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/** Static utility methods pertaining to creating {@link TokenBucketImpl} instances. */
public final class TokenBuckets
{
  private TokenBuckets() {}

  /**
   * Construct a token bucket that uses a fixed interval refill strategy.  Initially the bucket will start with
   * {@code capacityTokens} tokens in it, and every {@code period} time units {@code refillTokens} will be added to
   * it.  The tokens are added all at one time on the interval boundaries.  By default the system clock is used for
   * keeping time.
   *
   * @deprecated Use {@link org.isomorphism.util.TokenBuckets.Builder} instead.
   */
  public static TokenBucketImpl newFixedIntervalRefill(long capacityTokens, long refillTokens,
                                                       long period, TimeUnit unit)
  {
    return builder()
        .withCapacity(capacityTokens)
        .withYieldingSleepStrategy()
        .withFixedIntervalRefillStrategy(refillTokens, period, unit)
        .build();
  }

  public static Builder builder()
  {
    return new Builder();
  }

  public static class Builder
  {
    private Long capacity = null;
    private TokenBucketImpl.RefillStrategy refillStrategy = null;
    private TokenBucketImpl.SleepStrategy sleepStrategy = YIELDING_SLEEP_STRATEGY;
    private final Ticker ticker = Ticker.systemTicker();

    public Builder withCapacity(long numTokens)
    {
      checkArgument(numTokens > 0, "Must specify a positive number of tokens");
      capacity = numTokens;
      return this;
    }

    /** Refill tokens at a fixed interval. */
    public Builder withFixedIntervalRefillStrategy(long refillTokens, long period, TimeUnit unit)
    {
      refillStrategy = new FixedIntervalRefillStrategy(ticker, refillTokens, period, unit);
      return this;
    }

    /** Use a sleep strategy that will always attempt to yield the CPU to other processes. */
    public Builder withYieldingSleepStrategy()
    {
      sleepStrategy = YIELDING_SLEEP_STRATEGY;
      return this;
    }

    /**
     * Use a sleep strategy that will not yield the CPU to other processes.  It will busy wait until more tokens become
     * available.
     */
    public Builder withBusyWaitSleepStrategy() {
      sleepStrategy = BUSY_WAIT_SLEEP_STRATEGY;
      return this;
    }

    /** Build the token bucket. */
    public TokenBucketImpl build() {
      checkNotNull(capacity, "Must specify a capacity");
      checkNotNull(refillStrategy, "Must specify a refill strategy");

      return new TokenBucketImpl(capacity, refillStrategy, sleepStrategy);
    }
  }

  private static final TokenBucketImpl.SleepStrategy YIELDING_SLEEP_STRATEGY = new TokenBucketImpl.SleepStrategy()
  {
    @Override
    public void sleep()
    {
      // Sleep for the smallest unit of time possible just to relinquish control
      // and to allow other threads to run.
      Uninterruptibles.sleepUninterruptibly(1, TimeUnit.NANOSECONDS);
    }
  };

  private static final TokenBucketImpl.SleepStrategy BUSY_WAIT_SLEEP_STRATEGY = new TokenBucketImpl.SleepStrategy()
  {
    @Override
    public void sleep()
    {
      // Do nothing, don't sleep.
    }
  };
}

@@ -1050,838 +1050,8 @@
 %7B%7D%0A%0A
-  /**%0A   * Construct a token bucket that uses a fixed interval refill strategy.  Initially the bucket will start with%0A   * %7B@code capacityTokens%7D tokens in it, and every %7B@code period%7D time units %7B@code refillTokens%7D will be added to%0A   * it.  The tokens are added all at one time on the interval boundaries.  By default the system clock is used for%0A   * keeping time.%0A   *%0A   * @deprecated Use %7B@link org.isomorphism.util.TokenBuckets.Builder%7D instead.%0A   */%0A  public static TokenBucketImpl newFixedIntervalRefill(long capacityTokens, long refillTokens,%0A                                                       long period, TimeUnit unit)%0A  %7B%0A    return builder()%0A        .withCapacity(capacityTokens)%0A        .withYieldingSleepStrategy()%0A        .withFixedIntervalRefillStrategy(refillTokens, period, unit)%0A        .build();%0A  %7D%0A%0A
   pu
@@ -1080,16 +1080,16 @@
 ilder()%0A
+
   %7B%0A    
@@ -1736,16 +1736,27 @@
       re
+turn withRe
 fillStra
@@ -1759,19 +1759,17 @@
 Strategy
- = 
+(
 new Fixe
@@ -1827,16 +1827,216 @@
 d, unit)
+);%0A    %7D%0A%0A    /** Use a user defined refill strategy. */%0A    public Builder withRefillStrategy(TokenBucket.RefillStrategy refillStrategy)%0A    %7B%0A      this.refillStrategy = checkNotNull(refillStrategy)
 ;%0A      
@@ -2200,33 +2200,44 @@
 y()%0A    %7B%0A      
-s
+return withS
 leepStrategy = Y
@@ -2224,35 +2224,33 @@
 ithSleepStrategy
- = 
+(
 YIELDING_SLEEP_S
@@ -2256,35 +2256,17 @@
 STRATEGY
-;%0A      return this
+)
 ;%0A    %7D%0A
@@ -2466,16 +2466,20 @@
 rategy()
+%0A   
  %7B%0A     
@@ -2479,17 +2479,28 @@
 %7B%0A      
-s
+return withS
 leepStra
@@ -2503,19 +2503,17 @@
 Strategy
- = 
+(
 BUSY_WAI
@@ -2524,24 +2524,218 @@
 EEP_STRATEGY
+);%0A    %7D%0A%0A    /** Use a user defined sleep strategy. */%0A    public Builder withSleepStrategy(TokenBucket.SleepStrategy sleepStrategy)%0A    %7B%0A      this.sleepStrategy = checkNotNull(sleepStrategy)
 ;%0A      retu
@@ -2781,24 +2781,24 @@
  bucket. */%0A
-
     public T
@@ -2819,16 +2819,20 @@
  build()
+%0A   
  %7B%0A     

Refactor builder to allow custom refill and sleep strategies to be specified.