/**
 * Copyright 2005 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.Scorer;

/**
 * Eine einfache Beispielanwendung, um die Verwendung von Lucene zu verdeutlichen.
 * Indexiert Text- und PDF-Dateien und legt den erzeugten Index im Ordner
 * "luceneindex" im aktuellen Verzeichnis ab.
 * 
 * Aufruf auf der Kommandozeile mit index.sh/index.bat bzw. search.sh/search.bat.
 * 
 * @author Bernhard Messer, Daniel Naber
 * @version $rcs = ' $Id: LuceneDemo.java,v 1.13 2005/03/12 23:49:15 dnaber Exp $ ' ;
 */
public class LuceneDemo {

  // Ordner, in dem der neue Index angelegt werden soll:
  private final static String INDEX_DIR = "luceneindex";
  // Einfache Beispiel-Dokumente:
  private final static String EXAMPLE_DOC_DIR = "Dokumente";
  
  private final static String USAGE = "LuceneDemo -index <dokumentenverzeichnis>\n" +
      "oder:\n" +
      "LuceneDemo -search <suchanfrage>";

  // Standardkonstruktor verbieten:
  private LuceneDemo () {}
  
  public static void main(String[] args) throws IOException, ParseException {
    
    if (args.length > 0 && ! (args[0].equals("-index") || args[0].equals("-search"))) {
      System.err.println("Usage: " + USAGE);
      System.exit(1);
    }

    LuceneDemo ld = new LuceneDemo();
    if (args.length == 0) {
      System.out.println("Keine Argumente angegeben, Demo-Modus");
      System.out.println("Mögliche Aufrufe:");
      System.out.println(USAGE);
      File dirToIndex = new File(EXAMPLE_DOC_DIR);
      ld.indexDocuments(dirToIndex);
      // Keine Query angegeben, also Beispiel-Queries benutzen:
      // Einfache TermQuery:
      TermQuery tq = new TermQuery(new Term("contents", "schafherde"));
      ld.search(tq);
      // Wildcard Query:
      WildcardQuery wq = new WildcardQuery(new Term("contents", "schaf*"));
      ld.search(wq);
      // Fuzzy Query:
      FuzzyQuery fq = new FuzzyQuery(new Term("contents", "schaafherde"));
      ld.search(fq);
    } else {
      if (args.length < 2) {
        System.out.println("Fehlendes Argument (Suchanfrage bzw. Verzeichnis)");
        System.exit(1);
      }
      if (args[0].equals("-index")) {
        File dirToIndex = new File(args[1]);
        ld.indexDocuments(dirToIndex);
      } else {
        Query q = QueryParser.parse(args[1], "contents", new StandardAnalyzer());
        ld.search(q);
      }
    }
  }
  
  /**
   * Erzeugen des Lucene-Index.
   */
  private void indexDocuments(File startDir) throws IOException {
    Analyzer analyzer = new StandardAnalyzer();
    // Neuen IndexWriter im Verzeichnis INDEX_DIR anlegen.
    // Der Parameter true bedeutet, dass ein eventuell bestehender
    // Index gelöscht wird: 
    IndexWriter writer = new IndexWriter(INDEX_DIR, analyzer, true);
    indexDocumentsRecursive(startDir, writer);
    System.out.println(writer.docCount() + " Dokumente indexiert");
    // Index für die spätere Suche optimieren:
    writer.optimize();
    // allokierte Resourcen wieder freigeben:
    writer.close();
  }

  private void indexDocumentsRecursive(File file, IndexWriter writer) throws IOException {
    Document document = null;
    if (file.isDirectory()) {
      // ganzes Verzeichnis indexieren
      File[] files = file.listFiles();
      Arrays.sort(files);
      for (int i = 0; i < files.length; i++) {
        indexDocumentsRecursive(files[i], writer);
      }
    } else {
      // Konvertieren und Indexieren eines einzelnen Dokumentes:
      document = indexDocument(file);
      if (document != null) {
        // Dokument dem IndexWriter hinzufügen: 
        writer.addDocument(document);
      }
    }
  }
  
  private Document indexDocument(File file) throws IOException {
    String fileName = file.getAbsolutePath().toLowerCase();
    
    // Dieses kleine Beispiel indexiert alle Dateien deren Name die
    // mit "txt" oder "pdf" endet. Alle andere Dateien werden ignoriert.
    if (fileName.toLowerCase().endsWith("txt")) {
      System.err.println("Indexiere Text-Datei " + file.getAbsolutePath());
      return PlainTextDocument.getDocument(file);
    }
    else if (fileName.toLowerCase().endsWith("pdf")) {
      System.err.println("Indexiere PDF-Datei " + file.getAbsolutePath());
      return PDFDocument.getDocument(file);
    }
    else {
      System.err.println("Ignoriere Datei " + file.getAbsolutePath());
      return null;
    }
  }
  
  /**
   * Die Methode führt eine Suche über den zuvor erzeugten Index
   * aus. Das Ranking und eine Summary wird für das Trefferdokument 
   * ausgegeben.
   */
  private void search(Query query) throws IOException {
    // Instanz des StandardAnalyzers anlegen. Für Indizierung und 
    // Suche muss die gleiche Analyzer-Klasse verwendet werden:
    Analyzer analyzer = new StandardAnalyzer();
    // IndexSearcher-Instanz erzeugen für einen Index:
    Searcher searcher = new IndexSearcher(INDEX_DIR);
    
    // Suchen nach allen Dokumenten in deren Inhalt die 
    // gegebene Query vorkommt:
    Hits hits = searcher.search(query);

    System.out.println("\nSuchanfrage: " + query);
    System.out.println(hits.length() + " Treffer");

    // Scorer für den Highlighter anlegen.
    Scorer scorer = new QueryScorer(searcher.rewrite(query));
    Highlighter highlighter = new Highlighter (scorer);

    // Durch das Ergebnis iterieren und Treffer ausgeben:
    for (int i = 0; i < hits.length(); i++) {
      Document hitDoc = hits.doc(i);
      // Holen des Textes der im summary-Feld gespeichert ist:
      String bodyText = hitDoc.get("summary");
      String snippet = null;
      if (bodyText == null || bodyText.length() == 0) {
        snippet = null;
      } else {
        snippet = highlighter.getBestFragment(analyzer, "contents", bodyText);
      }
      // Dokument-Relevanz:
      float rank = hits.score(i);
      // Ermitteln der relevanten Textpassage:
      System.out.println("");
      System.out.println((i+1) + ". " +hitDoc.get("url")+ ", Relevanz: " + rank);
      if (snippet == null) {
        System.out.println("Keine Summary verfügbar");
      } else {
        snippet = snippet.replaceAll("[\n\r]+", " ");
        System.out.println("Treffer: ..." + snippet + "...");
      }
    }
    searcher.close();
  }

}
