/*******************************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.core.archive.compound;

import java.io.IOException;
import java.util.HashMap;

class BlockManager implements ArchiveConstants
{

	static int totalCacheSize = 0;

	BlockManagerEventAdapter eventAdapter;

	int poolSize;
	final int blockSize;
	HashMap blocks;
	Block firstBlock;
	Block lastBlock;

	BlockManager( BlockManagerEventAdapter adapter )
	{
		this( adapter, DEFAULT_BLOCK_SIZE );
	}

	BlockManager( BlockManagerEventAdapter adapter, int blockSize )
	{
		this.eventAdapter = adapter;
		this.blockSize = blockSize;
		this.poolSize = DEFAULT_BUFFER_POOL_SIZE;
		blocks = new HashMap( poolSize );
		firstBlock = null;
		lastBlock = null;
	}

	int getCacheSize( )
	{
		return poolSize * blockSize;
	}

	void setCacheSize( int cacheSize )
	{
		int blockCount = cacheSize / blockSize;
		if ( blockCount <= MIN_BUFFER_POOL_SIZE )
		{
			blockCount = MIN_BUFFER_POOL_SIZE;
		}
		this.poolSize = blockCount;
	}

	int getUsedCache( )
	{
		return blocks.size( ) * blockSize;
	}

	void reset( )
	{
		increaseTotalCacheSize( -blocks.size( ) * blockSize );
		blocks.clear( );
		firstBlock = null;
		lastBlock = null;
	}

	synchronized static void increaseTotalCacheSize( int size )
	{
		totalCacheSize += size;
	}

	void flush( ) throws IOException
	{
		if ( eventAdapter != null )
		{
			Block block = firstBlock;
			while ( block != null )
			{
				eventAdapter.flush( block );
				block = block.next;
			}
		}
	}

	Block getBlock( int id ) throws IOException
	{
		if ( firstBlock == null )
		{
			Block block = new Block( blockSize );
			increaseTotalCacheSize( blockSize );
			block.id = id;
			block.prev = null;
			block.next = null;
			firstBlock = lastBlock = block;
			blocks.put( new Integer( id ), block );
			if ( eventAdapter != null )
			{
				eventAdapter.refresh( block );
			}
			return block;
		}

		if ( id == firstBlock.id )
		{
			return firstBlock;
		}

		Block block = (Block) blocks.get( new Integer( id ) );
		if ( block == null )
		{
			if ( blocks.size( ) >= poolSize )
			{
				block = lastBlock;
				lastBlock = lastBlock.prev;
				lastBlock.next = null;
				blocks.remove( new Integer( block.id ) );
				if ( eventAdapter != null )
				{
					eventAdapter.flush( block );
				}
				block.reset( );
			}
			else
			{
				block = new Block( blockSize );
				increaseTotalCacheSize( blockSize );
			}
			block.id = id;
			blocks.put( new Integer( id ), block );
			if ( eventAdapter != null )
			{
				eventAdapter.refresh( block );
			}
		}
		else
		{
			block.prev.next = block.next;
			if ( lastBlock != block )
			{
				block.next.prev = block.prev;
			}
			else
			{
				lastBlock = block.prev;
			}
		}
		block.prev = null;
		block.next = firstBlock;
		firstBlock.prev = block;
		firstBlock = block;
		return block;
	}
}
