/*
 * Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
package com.opensymphony.oscache.base;


/**
 * Holds the state of a Cache Entry that is in the process of being (re)generated.
 * This is not synchronized; the synchronization must be handled by the calling
 * classes.
 *
 * @author <a href="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
 * @author Author: $
 * @version Revision: $
 */
public class EntryUpdateState {
    /**
     * The initial state when this object is first created
     */
    public static final int NOT_YET_UPDATING = -1;

    /**
     * Update in progress state
     */
    public static final int UPDATE_IN_PROGRESS = 0;

    /**
     * Update complete state
     */
    public static final int UPDATE_COMPLETE = 1;

    /**
     * Update cancelled state
     */
    public static final int UPDATE_CANCELLED = 2;

    /**
     * Current update state
     */
    int state = NOT_YET_UPDATING;
    
    /**
     * A counter of the number of threads that are coordinated through this instance. When this counter gets to zero, then the reference to this
     * instance may be released from the Cache instance.
     * This is counter is protected by the EntryStateUpdate instance monitor.
     */
    private int nbConcurrentUses = 1;

    /**
     * This is the initial state when an instance this object is first created.
     * It indicates that a cache entry needs updating, but no thread has claimed
     * responsibility for updating it yet.
     */
    public boolean isAwaitingUpdate() {
        return state == NOT_YET_UPDATING;
    }

    /**
     * The thread that was responsible for updating the cache entry (ie, the thread
     * that managed to grab the update lock) has decided to give up responsibility
     * for performing the update. OSCache will notify any other threads that are
     * waiting on the update so one of them can take over the responsibility.
     */
    public boolean isCancelled() {
        return state == UPDATE_CANCELLED;
    }

    /**
     * The update of the cache entry has been completed.
     */
    public boolean isComplete() {
        return state == UPDATE_COMPLETE;
    }

    /**
     * The cache entry is currently being generated by the thread that got hold of
     * the update lock.
     */
    public boolean isUpdating() {
        return state == UPDATE_IN_PROGRESS;
    }

    /**
     * Updates the state to <code>UPDATE_CANCELLED</code>. This should <em>only<em>
     * be called by the thread that managed to get the update lock.
     * @return the counter value after the operation completed
     */
    public int cancelUpdate() {
        if (state != UPDATE_IN_PROGRESS) {
            throw new IllegalStateException("Cannot cancel cache update - current state (" + state + ") is not UPDATE_IN_PROGRESS");
        }

        state = UPDATE_CANCELLED;
        return decrementUsageCounter();
    }

    /**
     * Updates the state to <code>UPDATE_COMPLETE</code>. This should <em>only</em>
     * be called by the thread that managed to get the update lock.
     * @return the counter value after the operation completed
     */
    public int completeUpdate() {
        if (state != UPDATE_IN_PROGRESS) {
            throw new IllegalStateException("Cannot complete cache update - current state (" + state + ") is not UPDATE_IN_PROGRESS");
        }

        state = UPDATE_COMPLETE;
        return decrementUsageCounter();
    }

    /**
     * Attempt to change the state to <code>UPDATE_IN_PROGRESS</code>. Calls
     * to this method must be synchronized on the EntryUpdateState instance.
     * @return the counter value after the operation completed
     */
    public int startUpdate() {
        if ((state != NOT_YET_UPDATING) && (state != UPDATE_CANCELLED)) {
            throw new IllegalStateException("Cannot begin cache update - current state (" + state + ") is not NOT_YET_UPDATING or UPDATE_CANCELLED");
        }

        state = UPDATE_IN_PROGRESS;
        return incrementUsageCounter();
    }

    /**
     * Increments the usage counter by one
     * @return the counter value after the increment
     */
	public synchronized int incrementUsageCounter() {
		nbConcurrentUses++;
		return nbConcurrentUses;
	}
	
    /**
     * Gets the current usage counter value
     * @return a positive number.
     */
	public synchronized int getUsageCounter() {
		return nbConcurrentUses;
	}
	
	
    /**
     * Decrements the usage counter by one. This method may only be called when the usage number is greater than zero
     * @return the counter value after the decrement
     */
	public synchronized int decrementUsageCounter() {
		if (nbConcurrentUses <=0) {
            throw new IllegalStateException("Cannot decrement usage counter, it is already equals to [" + nbConcurrentUses + "]");
		}
		nbConcurrentUses--;
		return nbConcurrentUses;
	}

	
}
