/**********************************************************************************
 * $URL: https://source.sakaiproject.org/svn/search/tags/sakai_2-3-1/search-impl/impl/src/java/org/sakaiproject/search/index/impl/FSIndexStorage.java $
 * $Id: FSIndexStorage.java 13933 2006-08-23 14:26:07Z ian@caret.cam.ac.uk $
 ***********************************************************************************
 *
 * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
 *
 * Licensed under the Educational Community License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/ecl1.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************/

package org.sakaiproject.search.index.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.FSDirectory;
import org.sakaiproject.search.api.SearchService;
import org.sakaiproject.search.index.AnalyzerFactory;
import org.sakaiproject.search.index.IndexStorage;

/**
 * A local only filestore implementation. This is a simple IndexImplementation
 * that performs all its indexing in a single, unoptimzed segment on the local 
 * filesystem
 * @author ieb
 *
 */
public class FSIndexStorage implements IndexStorage
{
	private static Log log = LogFactory.getLog(FSIndexStorage.class);

	protected String searchIndexDirectory = "searchindex";

	protected AnalyzerFactory analyzerFactory = null;

	protected boolean recoverCorruptedIndex = false;

	private long lastUpdate = System.currentTimeMillis();

	public void init() {
		
	}
	
	public void doPreIndexUpdate() throws IOException
	{
		log.debug("Starting process List on " + searchIndexDirectory);
		File f = new File(searchIndexDirectory);
		if (!f.exists())
		{
			f.mkdirs();
			log.debug("Indexing in " + f.getAbsolutePath());
		}

		if (IndexReader.isLocked(searchIndexDirectory))
		{
			// this could be dangerous, I am assuming that
			// the locking mechanism implemented here is
			// robust and
			// already prevents multiple modifiers.
			// A more

			IndexReader.unlock(FSDirectory.getDirectory(searchIndexDirectory,
					true));
			log.warn("Unlocked Lucene Directory for update, hope this is Ok");
		}
	}

	public IndexReader getIndexReader() throws IOException
	{
		return IndexReader.open(searchIndexDirectory);
	}

	public IndexWriter getIndexWriter(boolean create) throws IOException
	{
		return new IndexWriter(searchIndexDirectory, getAnalyzer(), create);
	}

	public void doPostIndexUpdate() throws IOException
	{
		lastUpdate = System.currentTimeMillis();

	}

	/**
	 * @return Returns the searchIndexDirectory.
	 */
	public String getSearchIndexDirectory()
	{
		return searchIndexDirectory;
	}


	public IndexSearcher getIndexSearcher() throws IOException
	{
		IndexSearcher indexSearcher = null;
		try
		{
			long reloadStart = System.currentTimeMillis();
			File indexDirectoryFile = new File(searchIndexDirectory);
			if (!indexDirectoryFile.exists())
			{
				indexDirectoryFile.mkdirs();
			}

			indexSearcher = new IndexSearcher(searchIndexDirectory);
			if (indexSearcher == null)
			{
				log.warn("No search Index exists at this time");

			}
			long reloadEnd = System.currentTimeMillis();
			log.info("Reload Complete " + indexSearcher.maxDoc() + " in "
					+ (reloadEnd - reloadStart));

		}
		catch (FileNotFoundException e)
		{
			log.error("There has been a major poblem with the"
					+ " Search Index which has become corrupted ", e);
			if (doIndexRecovery())
			{
				indexSearcher = new IndexSearcher(searchIndexDirectory);
			}
		}
		catch (IOException e)
		{
			log.error("There has been a major poblem with the "
					+ "Search Index which has become corrupted", e);
			if (doIndexRecovery())
			{
				indexSearcher = new IndexSearcher(searchIndexDirectory);
			}
		}
		return indexSearcher;
	}

	protected boolean doIndexRecovery() throws IOException
	{
		if (recoverCorruptedIndex)
		{
			IndexWriter iw = getIndexWriter(true);
			Document doc = new Document();
			String message = "Index Recovery performed on "
					+ (new Date()).toString();
			doc.add(new Field(SearchService.FIELD_CONTENTS, message, Field.Store.NO,
					Field.Index.TOKENIZED));
			iw.addDocument(doc);
			iw.close();
			log.error("Sucess fully recoverd From a corrupted index, "
					+ "the index will be empty and require a "
					+ "complete rebuild");
			return true;
		}
		return false;
	}

	public boolean indexExists()
	{
		return IndexReader.indexExists(searchIndexDirectory);
	}

	/**
	 * @return Returns the analyzerFactory.
	 */
	public AnalyzerFactory getAnalyzerFactory()
	{
		return analyzerFactory;
	}

	/**
	 * @param analyzerFactory
	 *        The analyzerFactory to set.
	 */
	public void setAnalyzerFactory(AnalyzerFactory analyzerFactory)
	{
		this.analyzerFactory = analyzerFactory;
	}

	public Analyzer getAnalyzer()
	{
		return analyzerFactory.newAnalyzer();
	}

	/**
	 * @return Returns the recoverCorruptedIndex.
	 */
	public boolean isRecoverCorruptedIndex()
	{
		return recoverCorruptedIndex;
	}

	/**
	 * @param recoverCorruptedIndex
	 *        The recoverCorruptedIndex to set.
	 */
	public void setRecoverCorruptedIndex(boolean recoverCorruptedIndex)
	{
		log.info("Using FSIndexStorage, storing the index "
				+ "on the local file system in " + searchIndexDirectory
				+ " if the index is corrupted recovery will "
				+ (recoverCorruptedIndex ? "" : "NOT ") + " be automatic");
		this.recoverCorruptedIndex = recoverCorruptedIndex;
	}

	public void setLocation(String location)
	{
		searchIndexDirectory = location;
		
	}

	public long getLastUpdate()
	{
		// not really relevant in the non cluster environment
		return lastUpdate;
	}

	public List getSegmentInfoList()
	{
		List l = new ArrayList();
		l.add(new Object[] {"Index Segment Info is not implemented for Local file system index stores","",""});
		return l;
	}

	public void closeIndexReader(IndexReader indexReader) throws IOException
	{
		if ( indexReader != null  ) 
		{
			indexReader.close();
		}
	}

	public void closeIndexWriter(IndexWriter indexWrite) throws IOException
	{
		if ( indexWrite != null ) 
		{
			indexWrite.close();
		}
		
	}
	
	public boolean isMultipleIndexers()
	{
		return false;
	}
	
	public void closeIndexSearcher(IndexSearcher indexSearcher)
	{
		IndexReader indexReader = indexSearcher.getIndexReader();
		try
		{
			indexReader.close();
		}
		catch (Exception ex)
		{
			log.error("Failed to close Index Reader "+ex.getMessage());
		}
		try
		{
			indexSearcher.close();
		}
		catch (Exception ex)
		{
			log.error("Failed to close Index Searcher "+ex.getMessage());

		}
	}



}
