// **********************************************************************
//
// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************

package Freeze;

public abstract class Index implements com.sleepycat.db.SecondaryKeyCreator
{
    //
    // Implementation details
    //
    
    public boolean
    createSecondaryKey(com.sleepycat.db.SecondaryDatabase secondary,
                       com.sleepycat.db.DatabaseEntry key,
                       com.sleepycat.db.DatabaseEntry value,
                       com.sleepycat.db.DatabaseEntry result)
        throws com.sleepycat.db.DatabaseException
    {
	Ice.Communicator communicator = _store.communicator();
	ObjectRecord rec = ObjectStore.unmarshalValue(value.getData(), communicator);

	byte[] secondaryKey = marshalKey(rec.servant);
	if(secondaryKey != null)
	{
	    result.setData(secondaryKey);
	    result.setSize(secondaryKey.length);
	    return true;
	}
	else
	{
	    //
	    // Don't want to index this one
	    //
            return false;
	}
    }

    public String
    name()
    {
	return _name;
    }

    public String
    facet()
    {
	return _facet;
    }

    protected Index(String name, String facet)
    {
	_name = name;
	_facet = facet;
    }

    protected abstract byte[]
    marshalKey(Ice.Object servant);
    
    protected Ice.Identity[]
    untypedFindFirst(byte[] k, int firstN)
    {
	EvictorI.DeactivateController deactivateController = _store.evictor().deactivateController();
	deactivateController.lock();
	
	try
	{
	    com.sleepycat.db.DatabaseEntry key = new com.sleepycat.db.DatabaseEntry(k);
	    
	    com.sleepycat.db.DatabaseEntry pkey = new com.sleepycat.db.DatabaseEntry();
	    com.sleepycat.db.DatabaseEntry value = new com.sleepycat.db.DatabaseEntry();
	    //
	    // dlen is 0, so we should not retrieve any value 
	    // 
	    value.setPartial(true);
	    
	    Ice.Communicator communicator = _store.communicator();
	    _store.evictor().saveNow();
	    
	    java.util.List identities;
	    
	    try
	    {
		for(;;)
		{
		    com.sleepycat.db.SecondaryCursor dbc = null;
		    identities = new java.util.ArrayList(); 
		    
		    try
		    {
			//
			// Move to the first record
			// 
			dbc = _db.openSecondaryCursor(null, null);
			boolean first = true;
			
			boolean found;
			
			do
			{
                            com.sleepycat.db.OperationStatus status;
                            if(first)
                            {
                                status = dbc.getSearchKey(key, pkey, value, null);
                            }
                            else
                            {
                                status = dbc.getNextDup(key, pkey, value, null);
                            }

			    found = status == com.sleepycat.db.OperationStatus.SUCCESS;
			    
			    if(found)
			    {
				Ice.Identity ident = ObjectStore.unmarshalKey(pkey.getData(), communicator);
				identities.add(ident);
				first = false;
			    }
			}
			while((firstN <= 0 || identities.size() < firstN) && found);
			
			break; // for(;;)
		    }
		    catch(com.sleepycat.db.DeadlockException dx)
		    {
			if(_store.evictor().deadlockWarning())
			{
			    communicator.getLogger().warning("Deadlock in Freeze.Index.untypedFindFirst while " +
                                                             "iterating over Db \"" + _store.evictor().filename() +
                                                             "/" + _dbName + "\"; retrying...");
			}
			
			//
			// Retry
			//
		    }
		    finally
		    {
			if(dbc != null)
			{
			    try
			    {
				dbc.close();
			    }
			    catch(com.sleepycat.db.DeadlockException dx)
			    {
				//
				// Ignored
				//
			    }
			}
		    }
		}
	    }
	    catch(com.sleepycat.db.DatabaseException dx)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(dx);
		ex.message = _store.evictor().errorPrefix() + "Db.cursor: " + dx.getMessage();
		throw ex;
	    }
	    
	    if(identities.size() != 0)
	    {
		Ice.Identity[] result = new Ice.Identity[identities.size()];
		return (Ice.Identity[]) identities.toArray(result);
	    }
	    else
	    {
		return new Ice.Identity[0];
	    }
	}
	finally
	{
	    deactivateController.unlock();
	}
    }
	
    protected Ice.Identity[]
    untypedFind(byte[] key)
    {
	return untypedFindFirst(key, 0);
    }

    protected int
    untypedCount(byte[] k)
    {
	EvictorI.DeactivateController deactivateController = _store.evictor().deactivateController();
	deactivateController.lock();
	
	try
	{
	    com.sleepycat.db.DatabaseEntry key = new com.sleepycat.db.DatabaseEntry(k);
	    com.sleepycat.db.DatabaseEntry value = new com.sleepycat.db.DatabaseEntry();
	    //
	    // dlen is 0, so we should not retrieve any value 
	    // 
	    value.setPartial(true);
	    _store.evictor().saveNow();
	    
	    try
	    {
		for(;;)
		{
		    com.sleepycat.db.Cursor dbc = null;
		    try
		    {
			dbc = _db.openCursor(null, null);   
			if(dbc.getSearchKey(key, value, null) == com.sleepycat.db.OperationStatus.SUCCESS)
			{
			    return dbc.count();
			}
			else
			{
			    return 0;
			}
		    }
		    catch(com.sleepycat.db.DeadlockException dx)
		    {
			if(_store.evictor().deadlockWarning())
			{
			    _store.communicator().getLogger().warning("Deadlock in Freeze.Index.untypedCount while " +
                                                                      "iterating over Db \"" +
                                                                      _store.evictor().filename() + "/" + _dbName +
                                                                      "\"; retrying...");
			}
			
			//
			// Retry
			//
		    }
		    finally
		    {
			if(dbc != null)
			{
			    try
			    {
				dbc.close();
			    }
			    catch(com.sleepycat.db.DeadlockException dx)
			    {
				//
				// Ignored
				//
			    }
			}
		    }
		}
	    }
	    catch(com.sleepycat.db.DatabaseException dx)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(dx);
		ex.message = _store.evictor().errorPrefix() + "Db.cursor: " + dx.getMessage();
		throw ex;
	    }
	}
	finally
	{
	    deactivateController.unlock();
	}
    }

    protected final Ice.Communicator
    communicator()
    {
	return _store.communicator();
    }
    
    void
    associate(ObjectStore store, com.sleepycat.db.Transaction txn, boolean createDb, boolean populateIndex)
	throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException
    {
	assert(txn != null);
	_store = store;
	
	_dbName = EvictorI.indexPrefix + store.dbName() + "." + _name;

        com.sleepycat.db.SecondaryConfig config = new com.sleepycat.db.SecondaryConfig();
        config.setAllowCreate(createDb);
        config.setAllowPopulate(populateIndex);
        config.setSortedDuplicates(true);
        config.setType(com.sleepycat.db.DatabaseType.BTREE);
	config.setKeyCreator(this);

	_db = _store.evictor().dbEnv().getEnv().openSecondaryDatabase(txn, _store.evictor().filename(), _dbName,
								      _store.db(), config);
    }

    void
    close()
    {
	if(_db != null)
	{
	    try
	    {
		_db.close();
	    }
	    catch(com.sleepycat.db.DatabaseException dx)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(dx);
		ex.message = _store.evictor().errorPrefix() + "Db.close: " + dx.getMessage();
		throw ex;
	    }
	    _db = null;   
	}
    }
  
    private final String _name;
    private final String _facet;
    private String _dbName;

    private com.sleepycat.db.SecondaryDatabase _db = null;
    private ObjectStore _store = null;
}
