// This software is released into the Public Domain.  See copying.txt for details.
package org.openstreetmap.osmosis.core.store;

import java.io.File;

import org.openstreetmap.osmosis.core.lifecycle.Completable;


/**
 * Provides a store for objects that can be located by a long identifier.
 * 
 * @param <T>
 *            The object type to be stored.
 * @author Brett Henderson
 */
public class IndexedObjectStore<T extends Storeable> implements Completable {
	private RandomAccessObjectStore<T> objectStore;
	private IndexStore<Long, LongLongIndexElement> indexStore;
	
	
	/**
	 * Creates a new instance.
	 * 
	 * @param serializationFactory
	 *            The factory defining the object serialisation implementation.
	 * @param tmpFilePrefix
	 *            The prefix of the storage file.
	 */
	public IndexedObjectStore(ObjectSerializationFactory serializationFactory, String tmpFilePrefix) {
		objectStore = new RandomAccessObjectStore<T>(serializationFactory, tmpFilePrefix + "d");
		
		indexStore = new IndexStore<Long, LongLongIndexElement>(
			LongLongIndexElement.class,
			new ComparableComparator<Long>(), 
			tmpFilePrefix + "i"
		);
	}
	
	
	/**
	 * Creates a new instance.
	 * 
	 * @param serializationFactory
	 *            The factory defining the object serialisation implementation.
	 * @param objectStorageFile
	 *            The storage file to use for objects.
	 * @param indexStorageFile
	 *            The storage file to use for the index.
	 */
	public IndexedObjectStore(
			ObjectSerializationFactory serializationFactory, File objectStorageFile, File indexStorageFile) {
		objectStore = new RandomAccessObjectStore<T>(serializationFactory, objectStorageFile);
		indexStore = new IndexStore<Long, LongLongIndexElement>(
			LongLongIndexElement.class,
			new ComparableComparator<Long>(),
			indexStorageFile
		);
	}
	
	
	/**
	 * Adds the specified object to the store.
	 * 
	 * @param id
	 *            The identifier allowing the object to be retrieved.
	 * @param data
	 *            The object to be added.
	 */
	public void add(long id, T data) {
		long objectOffset;
		
		// Write the object to the object store.
		objectOffset = objectStore.add(data);
		
		// Write the object offset keyed by the id to the index store.
		indexStore.write(new LongLongIndexElement(id, objectOffset));
	}
	
	
	/**
	 * Creates a new reader capable of accessing the contents of this store. The
	 * reader must be explicitly released when no longer required. Readers must
	 * be released prior to this store.
	 * 
	 * @return A store reader.
	 */
	public IndexedObjectStoreReader<T> createReader() {
		RandomAccessObjectStoreReader<T> objectStoreReader = null;
		
		objectStoreReader = objectStore.createReader();
		
		try {
			IndexStoreReader<Long, LongLongIndexElement> indexStoreReader;
			IndexedObjectStoreReader<T> reader;
			
			indexStoreReader = indexStore.createReader();
			
			reader = new IndexedObjectStoreReader<T>(objectStoreReader, indexStoreReader);
			
			objectStoreReader = null;
			
			return reader;
			
		} finally {
			if (objectStoreReader != null) {
				objectStoreReader.close();
			}
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void complete() {
		objectStore.complete();
		indexStore.complete();
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	public void close() {
		objectStore.close();
		indexStore.close();
	}
}
