/*
 * Hibernate Search, full-text search for your domain model
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.search.store.spi;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Properties;

import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.SleepingLockWrapper;
import org.hibernate.search.cfg.Environment;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.store.impl.DirectoryProviderHelper;
import org.hibernate.search.util.logging.impl.Log;
import org.hibernate.search.util.logging.impl.LoggerFactory;

/**
 * Provides utility functions around Lucene directories.
 *
 * @author Emmanuel Bernard
 * @author Sanne Grinovero
 * @author Hardy Ferentschik
 * @author Gunnar Morling
 */
public class DirectoryHelper {

	private static final Log log = LoggerFactory.make();

	private DirectoryHelper() {
	}

	/**
	 * Initialize the Lucene Directory if it isn't already.
	 *
	 * @param directory the Directory to initialize
	 * @throws SearchException in case of lock acquisition timeouts, IOException, or if a corrupt index is found
	 */
	public static void initializeIndexIfNeeded(Directory directory) {
		SimpleAnalyzer analyzer = new SimpleAnalyzer();
		try {
			if ( ! DirectoryReader.indexExists( directory ) ) {
				try {
					IndexWriterConfig iwriterConfig = new IndexWriterConfig( analyzer ).setOpenMode( OpenMode.CREATE_OR_APPEND );
					//Needs to have a timeout higher than zero to prevent race conditions over (network) RPCs
					//for distributed indexes (Infinispan but probably also NFS and similar)
					SleepingLockWrapper delayedDirectory = new SleepingLockWrapper( directory, 2000, 20 );
					IndexWriter iw = new IndexWriter( delayedDirectory, iwriterConfig );
					iw.close();
				}
				catch (LockObtainFailedException lofe) {
					log.lockingFailureDuringInitialization( directory.toString() );
				}
			}
		}
		catch (IOException e) {
			throw new SearchException( "Could not initialize index", e );
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Verify the index directory exists and is writable,
	 * or creates it if not existing.
	 *
	 * @param annotatedIndexName The index name declared on the @Indexed annotation
	 * @param properties The properties may override the indexname.
	 * @param verifyIsWritable Verify the directory is writable
	 * @return the Path representing the Index Directory
	 * @throws SearchException if any.
	 */
	public static Path getVerifiedIndexPath(String annotatedIndexName, Properties properties, boolean verifyIsWritable) {
		String indexBase = properties.getProperty( Environment.INDEX_BASE_PROP_NAME, "." );
		String indexName = properties.getProperty( Environment.INDEX_NAME_PROP_NAME, annotatedIndexName );
		Path baseIndexDir = FileSystems.getDefault().getPath( indexBase );
		DirectoryProviderHelper.makeSanityCheckedDirectory( baseIndexDir, indexName, verifyIsWritable );
		Path indexDir = baseIndexDir.resolve( indexName );
		DirectoryProviderHelper.makeSanityCheckedDirectory( indexDir, indexName, verifyIsWritable );
		return indexDir;
	}

}
