/*******************************************************************************
 * Copyright (c) 2005, 2008 QNX Software Systems and others.
 * 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:
 *    QNX - Initial API and implementation
 *    Markus Schorn (Wind River Systems)
 *    IBM Corporation
 *******************************************************************************/
package org.eclipse.cdt.internal.core.pdom.db;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.eclipse.core.runtime.CoreException;

/**
 * Caches the content of a piece of the database.
 */
final class Chunk {
	final private byte[] fBuffer= new byte[Database.CHUNK_SIZE];

	final Database fDatabase;
	final int fSequenceNumber;
	
	boolean fCacheHitFlag= false;
	boolean fDirty= false;
	boolean fLocked= false;	// locked chunks must not be released from cache.
	int fCacheIndex= -1;
		
	Chunk(Database db, int sequenceNumber) {
		fDatabase= db;
		fSequenceNumber= sequenceNumber;
	}

	void read() throws CoreException {
		try {
			final ByteBuffer buf= ByteBuffer.wrap(fBuffer);
			fDatabase.read(buf, fSequenceNumber*Database.CHUNK_SIZE);
		} catch (IOException e) {
			throw new CoreException(new DBStatus(e));
		}
	}

	void flush() throws CoreException {
		try {
			final ByteBuffer buf= ByteBuffer.wrap(fBuffer);
			fDatabase.write(buf, fSequenceNumber*Database.CHUNK_SIZE);
		} catch (IOException e) {
			throw new CoreException(new DBStatus(e));
		}
		fDirty= false;
	}

	public void putByte(final int offset, final byte value) {
		assert fLocked;
		fDirty= true;
		fBuffer[offset & Database.OFFSET_IN_CHUNK_MASK]= value;
	}
	
	public byte getByte(final int offset) {
		return fBuffer[offset & Database.OFFSET_IN_CHUNK_MASK];
	}
	
	public byte[] getBytes(final int offset, final int length) {
		final byte[] bytes = new byte[length];
		System.arraycopy(fBuffer, offset & Database.OFFSET_IN_CHUNK_MASK, bytes, 0, length);
		return bytes;
	}
	
	public void putBytes(final int offset, final byte[] bytes) {
		assert fLocked;
		fDirty= true;
		System.arraycopy(bytes, 0, fBuffer, offset & Database.OFFSET_IN_CHUNK_MASK, bytes.length);
	}
	
	public void putInt(final int offset, final int value) {
		assert fLocked;
		fDirty= true;
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		fBuffer[idx]=   (byte)(value >> 24);
		fBuffer[++idx]= (byte)(value >> 16);
		fBuffer[++idx]= (byte)(value >> 8);
		fBuffer[++idx]= (byte)(value);
	}
	
	public int getInt(final int offset) {
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		return ((fBuffer[idx] & 0xff) << 24) |
			((fBuffer[++idx] & 0xff) << 16) |
			((fBuffer[++idx] & 0xff) <<  8) |
			((fBuffer[++idx] & 0xff) <<  0);
	}

	public void put3ByteUnsignedInt(final int offset, final int value) {
		assert fLocked;
		fDirty= true;
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		fBuffer[idx]= (byte)(value >> 16);
		fBuffer[++idx]= (byte)(value >> 8);
		fBuffer[++idx]= (byte)(value);
	}
	
	public int get3ByteUnsignedInt(final int offset) {
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		return ((fBuffer[idx] & 0xff) << 16) |
			((fBuffer[++idx] & 0xff) <<  8) |
			((fBuffer[++idx] & 0xff) <<  0);
	}

	public void putShort(final int offset, final short value) {
		assert fLocked;
		fDirty= true;
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		fBuffer[idx]= (byte)(value >> 8);
		fBuffer[++idx]= (byte)(value);
	}
	
	public short getShort(final int offset) {
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		return (short) (((fBuffer[idx] << 8) | (fBuffer[++idx] & 0xff)));
	}

	public long getLong(final int offset) {
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		return ((((long)fBuffer[idx] & 0xff) << 56) |
				(((long)fBuffer[++idx] & 0xff) << 48) |
				(((long)fBuffer[++idx] & 0xff) << 40) |
				(((long)fBuffer[++idx] & 0xff) << 32) |
				(((long)fBuffer[++idx] & 0xff) << 24) |
				(((long)fBuffer[++idx] & 0xff) << 16) |
				(((long)fBuffer[++idx] & 0xff) <<  8) |
				(((long)fBuffer[++idx] & 0xff) <<  0));
	}

	public void putLong(final int offset, final long value) {
		assert fLocked;
		fDirty= true;
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;

		fBuffer[idx]=   (byte)(value >> 56);
		fBuffer[++idx]= (byte)(value >> 48);
		fBuffer[++idx]= (byte)(value >> 40);
		fBuffer[++idx]= (byte)(value >> 32);
		fBuffer[++idx]= (byte)(value >> 24);
		fBuffer[++idx]= (byte)(value >> 16);
		fBuffer[++idx]= (byte)(value >> 8);
		fBuffer[++idx]= (byte)(value);
	}
	
	public void putChar(final int offset, final char value) {
		assert fLocked;
		fDirty= true;
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		fBuffer[idx]= (byte)(value >> 8);
		fBuffer[++idx]= (byte)(value);
	}
	
	public char getChar(final int offset) {
		int idx= offset & Database.OFFSET_IN_CHUNK_MASK;
		return (char) (((fBuffer[idx] << 8) | (fBuffer[++idx] & 0xff)));
	}

	public void getCharArray(final int offset, final char[] result) {
		final ByteBuffer buf= ByteBuffer.wrap(fBuffer);
		buf.position(offset & Database.OFFSET_IN_CHUNK_MASK);
		buf.asCharBuffer().get(result);
	}
	
	void clear(final int offset, final int length) {
		assert fLocked;
		fDirty= true;
		int idx= (offset & Database.OFFSET_IN_CHUNK_MASK);
		final int end= idx + length;
		for (; idx < end; idx++) {
			fBuffer[idx]= 0;
		}
	}
}
