/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     MetaMatrix, Inc - repackaging and updates for use as a metadata store
 *******************************************************************************/
package org.teiid.internal.core.index;

import java.io.IOException;

import org.teiid.core.index.IEntryResult;
import org.teiid.core.index.IIndex;
import org.teiid.core.index.IQueryResult;
import org.teiid.metadata.VDBResource;


/**
 * An Index is used to create an index on the disk, and to make queries. It uses a set of 
 * indexers and a mergeFactory. The index fills an inMemoryIndex up 
 * to it reaches a certain size, and then merges it with a main index on the disk.
 * <br> <br>
 * The changes are only taken into account by the queries after a merge.
 */

public class Index implements IIndex {
	/**
	 * Maximum size of the index in memory.
	 */
	public static final int MAX_FOOTPRINT= 10000000;

	private VDBResource indexFile;
    
    /* 
     * Caching the index input object so we can keep it open for multiple pass querying rather than
     * opening/closing and wasting CPU for file IO
     */
    private BlocksIndexInput cachedInput;
    protected boolean doCache = false;
    private String resourceFileName;

	/**
	 * String representation of this index.
	 */
	public String toString;
    
	public Index(VDBResource f) throws IOException {
		indexFile = f;
		initialize();
	}

	/**
	 * @see IIndex#getNumDocuments
	 */
	public int getNumDocuments() throws IOException {
        BlocksIndexInput input = getBlocksIndexInput();
		try {
            input.open();
			return input.getNumFiles();
		} finally {
            if( !doCache ) {
                input.close();
            }
		}		
	}
    
	/**
	 * @see IIndex#getNumWords
	 */
	public int getNumWords() throws IOException {
        BlocksIndexInput input = getBlocksIndexInput();
		try {
            input.open();
			return input.getNumWords();
		} finally {
            if( !doCache ) {
                input.close();
            }
		}		
	}
    
	/**
	 * Initialises the indexGenerator.
	 */
	public void initialize() throws IOException {
		
		// check whether existing index file can be read
        BlocksIndexInput mainIndexInput= getBlocksIndexInput();
		try {
			mainIndexInput.open();
		} catch(IOException e) {
			BlocksIndexInput input = mainIndexInput;
			try {
				input.setOpen(true);
				input.close();
			} finally {
                input.setOpen(false);
			}
            //System.out.println(" Index.initialize(): Deleting Index file = " + indexFile.getName());
			mainIndexInput = null;
			throw e;
		}
        if( !doCache ) {
            mainIndexInput.close();
        }
	}
    
	/**
	 * @see IIndex#query
	 */
	public IQueryResult[] query(String word) throws IOException {
        BlocksIndexInput input= getBlocksIndexInput();
		try {
			return input.query(word);
		} finally {
            if( !doCache ) {
                input.close();
            }
		}
	}
    
	public IEntryResult[] queryEntries(char[] prefix) throws IOException {
        BlocksIndexInput input= getBlocksIndexInput();
		try {
			return input.queryEntriesPrefixedBy(prefix);
		} finally {
            if( !doCache ) {
                input.close();
            }
		}
	}
    
	/**
	 * @see IIndex#queryInDocumentNames
	 */
	public IQueryResult[] queryInDocumentNames(String word) throws IOException {
        BlocksIndexInput input= getBlocksIndexInput();
		try {
			return input.queryInDocumentNames(word);
		} finally {
            if( !doCache ) {
                input.close();
            }
		}
	}
    
	/**
	 * @see IIndex#queryPrefix
	 */
	public IQueryResult[] queryPrefix(char[] prefix) throws IOException {
        BlocksIndexInput input= getBlocksIndexInput();
		try {
			return input.queryFilesReferringToPrefix(prefix);
		} finally {
            if( !doCache ) {
                input.close();
            }
		}
	}
    
    /**
     * Overloaded the method in Index to allow a user to specify if the 
     * query should be case sensitive.
     */
    public IEntryResult[] queryEntriesMatching(char[] prefix, boolean isCaseSensitive) throws IOException {
        BlocksIndexInput input= getBlocksIndexInput();
        try {
            return input.queryEntriesMatching(prefix,isCaseSensitive);
        } finally {
            if( !doCache ) {
                input.close();
            }
        }
    }
    
    /**
     * Overloaded the method in Index to allow a user to specify if the 
     * query should be case sensitive.
     */
    public IEntryResult[] queryEntries(char[] prefix, boolean isCaseSensitive) throws IOException {
        BlocksIndexInput input = getBlocksIndexInput();
        try {
            return input.queryEntriesPrefixedBy(prefix,isCaseSensitive);
        } finally {
            if( !doCache ) {
                input.close();
            }
        }
    }    
    
	protected BlocksIndexInput getBlocksIndexInput() {
        if( doCache ) {
            if( getCachedInput() == null  ) {
                boolean wasLoaded = false;
                try {
                    if( getCachedInput() == null ) {
                        setCachedInput( new BlocksIndexInput(indexFile) );
                        getCachedInput().open();
                        wasLoaded = true;
                    }
                } catch ( IOException theException ) {
                    
                } finally {
                    if( wasLoaded && getCachedInput() != null ) {
                        return getCachedInput();
                    }
                    setCachedInput(null);
                }
            } else {
                return getCachedInput();
            }
        }
        
        return new BlocksIndexInput(indexFile);
    }
    
    public void close() {
        if( getCachedInput() != null ) {
            try {
                getCachedInput().close();
            } catch (IOException theException) {
            } finally {
                setCachedInput(null);
            }
        }
    }
    
    public String toString() {
    	String str = this.toString;
    	if (str == null) str = super.toString();
		str += "(length: "+ indexFile.getSize() +")"; //$NON-NLS-1$ //$NON-NLS-2$
    	return str;
    }
    
    public void setDoCache(boolean theDoCache) {
        this.doCache = theDoCache;
    }
    
    public BlocksIndexInput getCachedInput() {
        return this.cachedInput;
    }
    
    public void setCachedInput(BlocksIndexInput theCachedInput) {
        this.cachedInput = theCachedInput;
    }
    
    public String getResourceFileName() {
        return this.resourceFileName;
    }
}
