/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.extender.internal.util.concurrent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Simple counting class which can be incremented or decremented in a
 * synchronized manner. This class can be used as a synchronization mechanism
 * between threads mainly though {@link #waitForZero(long)} method.
 * 
 * The main usage of the class is to allow a master thread, to know when other
 * threads (slaves) have passed a certain point in execution.
 * 
 * <p/> As opposed to a Barrier or a Semaphore, this class should be used only
 * with 1 waiting thread (a master) and any number of slave threads.
 * 
 * <pre style="code">
 * Thread 1:
 *  counter.increment();
 *  thread2.start();
 *  counter.increment();
 *  thread3.start();
 *   
 *  // wait 1 second for other threads to complete
 *  counter.waitForZero(1000);
 * 
 * Thread 2:
 *  // do some work
 *  counter.decrement();
 * 
 * Thread 3:
 *  // do some work
 *  counter.decrement();
 * 
 * </pre>
 * 
 * <p/> Mainly for usage inside the framework. All methods are thread-safe
 * however for the master/slave pattern, synchronized blocks are recommended as
 * multiple operations have to be executed at once.
 * 
 * @author Costin Leau
 * 
 */
public class Counter {

	private int counter = 0;

	private static final Log log = LogFactory.getLog(Counter.class);

	private final String name;


	/**
	 * Create counter with a given name.
	 * 
	 * @param name counter name
	 */
	public Counter(String name) {
		this.name = name;
	}

	/**
	 * Increment the counter value.
	 */
	public synchronized void increment() {
		counter++;
		if (log.isTraceEnabled())
			log.trace("counter [" + name + "] incremented to " + counter);
	}

	/**
	 * Decrement the counter value.
	 */
	public synchronized void decrement() {
		counter--;
		if (log.isTraceEnabled())
			log.trace("counter [" + name + "] decremented to " + counter);
		notifyAll();
	}

	public synchronized boolean decrementAndWait(long timeToWait) {
		decrement();
		if (counter > 0)
			return waitForZero(timeToWait);
		return true;
	}

	/**
	 * Check if the counter value is zero.
	 * 
	 * @return true if value is equal or below zero, false otherwise.
	 */
	public synchronized boolean isZero() {
		return is(0);
	}

	public synchronized boolean is(int value) {
		return counter == value;
	}

	/**
	 * Return the counter value.
	 * 
	 * @return the counter value.
	 */
	public synchronized int getValue() {
		return counter;
	}

	public synchronized String toString() {
		return "" + counter;
	}

	/**
	 * Specialized method which waits for 0. Identical to waitFor(0, waitTime).
	 * 
	 * @see #waitFor(int, long)
	 * @param waitTime
	 * @return true if the waiting timed out, false otherwise
	 */
	public synchronized boolean waitForZero(long waitTime) {
		return waitFor(0, waitTime);
	}

	/**
	 * Wait maximum the given amount of time, for the counter to reach the given
	 * value. This mechanism relies on {@link Object#wait(long)} and
	 * {@link Object#notify()} mechanism to work appropriately. Please see the
	 * class javadoc for more info.
	 * 
	 * <p/> This method will stop waiting and return true if the thread is
	 * interrupted.
	 * 
	 * @param value the value to wait for
	 * @param waitTime the time (in miliseconds) to wait for zero value
	 * @return true if the waiting timed out, false otherwise
	 */
	public synchronized boolean waitFor(int value, long waitTime) {
		boolean timedout = false;
		long remainingTime = waitTime;
		long startTime = System.currentTimeMillis();

		while (counter > value && !timedout) {
			// start waiting
			try {
				this.wait(remainingTime);
				// compute the remaining time
				remainingTime = waitTime - (System.currentTimeMillis() - startTime);
				timedout = remainingTime <= 0;
			}
			catch (InterruptedException ex) {
				timedout = true;
			}
		}

		return timedout;
	}
}
