/* Copyright (C) 2004 - 2008  db4objects Inc.  http://www.db4o.com

This file is part of the db4o open source object database.

db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */
package com.db4o.internal.cs;

import com.db4o.foundation.*;
import com.db4o.internal.query.result.*;

/**
 * @exclude
 */
class ClientQueryResultIterator implements Iterator4 {
	
	private static final PrefetchingStrategy _prefetchingStrategy = SingleMessagePrefetchingStrategy.INSTANCE;
	
	private Object[] _prefetchedObjects;
	private int _remainingObjects;
	private int _prefetchRight;
	private final AbstractQueryResult _client;
	private final IntIterator4 _ids;
	
	ClientQueryResultIterator(AbstractQueryResult client) {
		_client = client;
		_ids = client.iterateIDs();
	}

	public Object current() {
		synchronized (streamLock()) {
			return _client.activate(prefetchedCurrent());
		}
	}

	private Object streamLock() {
		return _client.lock();
	}
	
	public void reset() {
		_remainingObjects = 0;
		_ids.reset();
	}

	public boolean moveNext() {
		synchronized (streamLock()) {
			if (_remainingObjects > 0) {
				--_remainingObjects;
				return skipNulls();
			}
			
			prefetch();
			
			--_remainingObjects;
			if(_remainingObjects < 0){
				return false;
			}
			return skipNulls();
		}
	}

	private boolean skipNulls() {
		// skip nulls (deleted objects)
		if (prefetchedCurrent() == null){
			return moveNext();
		}
		return true;
	}

	private void prefetch() {
		ensureObjectCacheAllocated(prefetchCount());
		_remainingObjects = _prefetchingStrategy.prefetchObjects(stream(), _ids, _prefetchedObjects, prefetchCount());
		_prefetchRight=_remainingObjects;
	}

	private int prefetchCount() {
		return stream().config().prefetchObjectCount();
	}

	private ClientObjectContainer stream() {
		return (ClientObjectContainer) _client.stream();
	}

	private Object prefetchedCurrent() {
		return _prefetchedObjects[_prefetchRight-_remainingObjects-1];
	}
	
	// TODO: open this as an external tuning interface in ExtObjectSet
	
//	public void prefetch(int count){
//		if(count < 1){
//			count = 1;
//		}
//		i_prefetchCount = count;
//		Object[] temp = new Object[i_prefetchCount];
//		if(i_remainingObjects > 0){
//			// Potential problem here: 
//			// On reducing the prefetch size, this will crash.
//			System.arraycopy(i_prefetched, 0, temp, 0, i_remainingObjects);
//		}
//		i_prefetched = temp;
//	}

	private void ensureObjectCacheAllocated(int prefetchObjectCount) {
		if(_prefetchedObjects==null) {
			_prefetchedObjects = new Object[prefetchObjectCount];
			return;
		}
		if(prefetchObjectCount>_prefetchedObjects.length) {
			Object[] newPrefetchedObjects=new Object[prefetchObjectCount];
			System.arraycopy(_prefetchedObjects, 0, newPrefetchedObjects, 0, _prefetchedObjects.length);
			_prefetchedObjects=newPrefetchedObjects;
		}
	}


}
