/*******************************************************************************
 * 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;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.birt.core.util.IOUtil;

public class RAStreamBufferMgr {
	
	private RandomAccessFile randomFile;
	
	// The total number of buffer has been allocated. 
	private int totalBuffer;
	
	// An arraylist to keep the buffer in order.
	// When MAX_NUMBER_OF_STREAM_BUFFER has been reached, we reuse the
	// buffer from the beginning of the list. There is less chance they
	// are going to be visited again.
	private List bufferList = new ArrayList( IOUtil.MAX_NUMBER_OF_STREAM_BUFFER );
	
	// A hash table to map between offset and buffer
	private Map bufferMap = new HashMap();
	
	// The buffer will be used for next operation
	private RAStreamBuffer currentBuffer;
	
	public RAStreamBufferMgr( RandomAccessFile randomFile ) throws IOException
	{
		this.randomFile = randomFile;
		this.totalBuffer = 0;
		this.currentBuffer = getBuffer( 0 );
	}
	
	/*
	 * The file pointer in the underlying file if no buffer is used.
	 */
	public long getFilePointer( )
	{
		return currentBuffer.getOffset() + currentBuffer.getBufCur();
	}
	
	/*
	 * Write the data in array b[], if current buffer is not enough to hold
	 * all the data, a new buffer will be allocated or an old buffer will
	 * be reused.
	 */
	public void write (byte b[], int off, int len ) throws IOException
	{
		while ( len > 0 )
		{
			int ret = currentBuffer.write( b, off, len );
			len -= ret;
			off += ret;
			if ( len > 0)
				currentBuffer = getBuffer( currentBuffer.getOffset() 
						+ IOUtil.RA_STREAM_BUFFER_LENGTH);
		}
	}

	public void seek( long localPos ) throws IOException
	{
		long offset = ( localPos / IOUtil.RA_STREAM_BUFFER_LENGTH ) 
							* IOUtil.RA_STREAM_BUFFER_LENGTH;
		if ( currentBuffer.getOffset() != offset )
			currentBuffer = getBuffer( offset );
		currentBuffer.setBufCur( (int)(localPos - offset) );
	}

	/*
	 * Flush all the buffers
	 */
	public void flushBuffer() throws IOException
	{
		for ( int i = 0; i < totalBuffer; i++ )
		{
			RAStreamBuffer buffer = (RAStreamBuffer)bufferList.get(i);
			buffer.flushBuffer();
		}
	}
	
	
	/**
	 * Get next available buffer for the data from position offset.
	 * @param offset
	 * @return
	 * @throws IOException
	 */
	private RAStreamBuffer getBuffer( long offset ) throws IOException
	{
		// If we already have a buffer allocated for that offset, just 
		// return it.
		RAStreamBuffer buffer = (RAStreamBuffer)bufferMap.get( offset );
		if ( buffer != null )
			return buffer;
		// If not, and MAX_NUMBER_OF_STREAM_BUFFER has not been reached, 
		// allocate a new buffer for it.
		if ( totalBuffer < IOUtil.MAX_NUMBER_OF_STREAM_BUFFER )
		{
			buffer = new RAStreamBuffer( this.randomFile );
			buffer.resetBuffer( offset );
			totalBuffer ++;
			bufferList.add( buffer );
			bufferMap.put(offset, buffer);
			return buffer;
		}
		
		// If no buffer has been found, and MAX_NUMBER_OF_STREAM_BUFFER has
		// been reached, reuse the buffer from the beginning of the list and
		// put the buffer to the end of the list.
		buffer = (RAStreamBuffer)bufferList.get( 0 );
		buffer.flushBuffer();
		bufferMap.remove( buffer.getOffset());
		buffer.resetBuffer( offset ); 
		bufferMap.put( offset, buffer);
		bufferList.remove( 0 );
		bufferList.add( buffer );
		
		return buffer;
	}
	
}

@@ -3041,16 +3041,53 @@
 urn it.%0A
+%09%09Long offsetKey = new Long(offset);%0A
 %09%09RAStre
@@ -3137,24 +3137,27 @@
 .get( offset
+Key
  );%0A%09%09if ( b
@@ -3506,24 +3506,27 @@
 p.put(offset
+Key
 , buffer);%0A%09
@@ -3826,16 +3826,25 @@
 remove( 
+new Long(
 buffer.g
@@ -3854,16 +3854,18 @@
 ffset())
+ )
 ;%0A%09%09buff
@@ -3891,16 +3891,16 @@
 set ); %0A
-
 %09%09buffer
@@ -3914,16 +3914,19 @@
 ( offset
+Key
 , buffer

fix a bug caused by the uncompatiability of JDK1.4 and JDK1.5