/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance *
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.apache.streams.util.oauth.tokens.tokenmanager.impl;

import org.apache.streams.util.oauth.tokens.tokenmanager.SimpleTokenManager;

import java.util.ArrayList;
import java.util.Collection;

/**
 * Manages a pool of tokens the most basic possible way.
 * If all tokens are added to the manager before {@link BasicTokenManager#getNextAvailableToken() getNextAvailableToken}
 * is called tokens are issued in the order they were added to the manager, FIFO.  The BasicTokenManager acts as a circular queue
 * of tokens.  Once the manager issues all available tokens it will cycle back to the first token and start issuing tokens again.
 *
 * </p>
 * When adding tokens to the pool of available tokens, the manager will not add tokens that are already in the pool.
 *
 * <p/>
 * The manager class is thread safe.
 */
public class BasicTokenManager<T> implements SimpleTokenManager<T> {

  private ArrayList<T> availableTokens;
  private int nextToken;

  public BasicTokenManager() {
    this(null);
  }

  /**
   * BasicTokenManager constructor.
   * @param tokens Collection of tokens
   */
  public BasicTokenManager(Collection<T> tokens) {
    if (tokens != null) {
      this.availableTokens = new ArrayList<>(tokens.size());
      this.addAllTokensToPool(tokens);
    } else {
      this.availableTokens = new ArrayList<>();
    }
    this.nextToken = 0;
  }

  @Override
  public synchronized boolean addTokenToPool(T token) {
    return !(token == null || this.availableTokens.contains(token)) && this.availableTokens.add(token);
  }

  @Override
  public synchronized boolean addAllTokensToPool(Collection<T> tokens) {
    int startSize = this.availableTokens.size();
    for (T token : tokens) {
      this.addTokenToPool(token);
    }
    return startSize < this.availableTokens.size();
  }

  @Override
  public synchronized T getNextAvailableToken() {
    T token;
    if (this.availableTokens.size() == 0) {
      return null;
    } else {
      token = this.availableTokens.get(nextToken++);
      if (nextToken == this.availableTokens.size()) {
        nextToken = 0;
      }
      return token;
    }
  }

  @Override
  public synchronized int numAvailableTokens() {
    return this.availableTokens.size();
  }
}
