// CacheState.java
// $Id: CacheState.java,v 1.7 1997/02/12 09:48:06 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html


package w3c.www.protocol.http.cache;

import java.net.*;
import java.util.*;
import java.io.*;

import w3c.tools.store.*;
import w3c.www.http.*;
import w3c.www.protocol.http.*;

public class CacheState extends Resource {
    protected static final int GENERATIONS_HISTORY_SIZE = 10;

    /**
     * Attribute index - The counter, to use for file naming.
     */
    protected static int ATTR_COUNTER = -1;
    /**
     * Attribute index - The number of bytes of cache disk used.
     */
    protected static int ATTR_CACHE_USED = -1;
    /**
     * Attribute index - The cache sweeper current generation.
     */
    protected static int ATTR_GENERATION = -1;
    /**
     * Attribute index - The current generation sizes.
     */
    protected static int ATTR_GENERATIONS_SIZE = -1;
    /**
     * Attribute index - The base generation we are using.
     */
    protected static int ATTR_BASE_GENERATION = -1;

    static {
	Attribute a = null;
	Class     c = null;
	try {
	    c = Class.forName("w3c.www.protocol.http.cache.CacheState");
	} catch (Exception ex) {
	    ex.printStackTrace();
	    System.exit(1);
	}
	// Declare the counter attribute:
	a = new IntegerAttribute("counter"
				 , new Integer(0)
				 , Attribute.COMPUTED);
	ATTR_COUNTER = AttributeRegistry.registerAttribute(c, a);
	// Declare the numnber of bytes used.
	a = new IntegerAttribute("cache-used"
				 , new Integer(0)
				 , Attribute.COMPUTED);
	ATTR_CACHE_USED = AttributeRegistry.registerAttribute(c, a);
	// Declare the sweeper's generation count:
	a = new IntegerAttribute("generation"
				 , new Integer(0)
				 , Attribute.COMPUTED);
	ATTR_GENERATION = AttributeRegistry.registerAttribute(c, a);
	// Declare the generations size attribute:
	a = new IntegerArrayAttribute("generations-size"
				      , null
				      , Attribute.COMPUTED);
	ATTR_GENERATIONS_SIZE = AttributeRegistry.registerAttribute(c, a);
	// Declare the base-generation attribute:
	a = new IntegerAttribute("base-generation"
				 , null
				 , Attribute.COMPUTED);
	ATTR_BASE_GENERATION = AttributeRegistry.registerAttribute(c, a);
    }

    public synchronized int getNext() {
	int i = getInt(ATTR_COUNTER, -1);
	setInt(ATTR_COUNTER, i+1);
	return i;
    }

    public synchronized int getCacheUsed() {
	return getInt(ATTR_CACHE_USED, 0);
    }

    /**
     * Get the current collection generation number.
     * @return An integer, giving the current collector generation.
     */

    public int getGeneration() {
	return getInt(ATTR_GENERATION, 0);
    }

    public int getBaseGeneration() {
	return getInt(ATTR_BASE_GENERATION, 0);
    }

    public void setBaseGeneration(int newbase) {
	setInt(ATTR_BASE_GENERATION, newbase);
    }

    public int[] getGenerationsSize() {
	return (int[]) getValue(ATTR_GENERATIONS_SIZE, null);
    }

    public void setGenerationsSize(int sizes[]) {
	setValue(ATTR_GENERATIONS_SIZE, sizes);
    }

    public synchronized void shiftGenerations(int g) {
	// Update the base generation and then slide generation sizes:
	int gensize[] = getGenerationsSize();
	int genlength = gensize.length;
	if ( g >= genlength ) {
	    setBaseGeneration(g-genlength+1);
	    System.arraycopy(gensize, 1, gensize, 0, genlength-1);
	    gensize[genlength-1] = 0;
	    setGenerationsSize(gensize);
	}
    }

    /**
     * Get the next collector generation number, and update state.
     * @return The next (which will be current when the method returns) 
     * collector generation.
     */

    public synchronized int setNextGeneration() {
	// Increment the generation count:
	int g = getGeneration()+1;
	setInt(ATTR_GENERATION, g);
	// Update the base generation and then slide generation sizes:
	shiftGenerations(g);
	return g;
    }

    /**
     * Update generations size
     * @param generation The generation whose size is changing.
     * @param size The delta with previous size.
     */

    protected synchronized void updateGenerationSize(int generation
						     , int size) {
	// Compute offsets in the generations size array:
	int gensize[] = getGenerationsSize();
	int basegen   = getBaseGeneration();
	int idx       = -1;
	if ((idx = generation-basegen) >= 0) 
	    gensize[idx] += size;
	markModified();
    }

    /**
     * Get current generation size.
     * @return The size in bytes of the current generation.
     */

    protected synchronized int getCurrentGenerationSize() {
	int gensize[] = getGenerationsSize();
	int curgen    = getGeneration();
	int basegen   = getBaseGeneration();
	return gensize[curgen-basegen];
    }

    /**
     * Compute generation number to sweep, given  amount of data to collect.
     * @param size The size of data to be collected.
     */

    protected synchronized int computeCollectGeneration(int size) {
	int gensize[]   = getGenerationsSize();
	int collectible = 0;
	int i           = 0;
	while (i < gensize.length) {
	    int sz = gensize[i];
	    if ( sz > 0 ) {
		if ((collectible += sz) >= size)
		    break;
	    }
	    i++;
	}
	int gen = getBaseGeneration()+i+1;
        return gen;
    }

    /**
     * Update cache space counter.
     * @param size The number of bytes used (if positif), or the number of
     * bytes freed (if negative).
     * @return The total number of bytes used.
     */

    public synchronized int updateCacheSpaceCounter(int size) {
	int used = getCacheUsed() + size;
	setInt(ATTR_CACHE_USED, used);
	return used;
    }


    public CacheState() {
	super();
    }

    public void print(PrintStream out) {
	int curgen  = getGeneration();
	int basegen = getBaseGeneration();
	out.println("# Number of bytes comsumed: "+getCacheUsed());
	out.println("# Current generation: "+curgen);
	out.println("# Generation base: "+basegen);
	int gensize[] = getGenerationsSize();
	for (int i = 0 ; i < gensize.length; i++) {
	    out.println("#\tgeneration["+(basegen+i)+"]="
			+ gensize[i] + " bytes.");
	}
    }

    public void initialize(Object values[]) {
	super.initialize(values);
	if (getGenerationsSize() == null) {
	    int generations[] = new int[GENERATIONS_HISTORY_SIZE];
	    setValue(ATTR_GENERATIONS_SIZE, generations);
	}
    }

}

