/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

package org.apache.lucene.analysis.opennlp;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.apache.lucene.analysis.custom.CustomAnalyzer;
import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilterFactory;
import org.apache.lucene.analysis.miscellaneous.RemoveDuplicatesTokenFilterFactory;
import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase;
import org.apache.lucene.util.ClasspathResourceLoader;

public class TestOpenNLPLemmatizerFilterFactory extends BaseTokenStreamTestCase {

  private static final String SENTENCE = "They sent him running in the evening.";
  private static final String[] SENTENCE_dict_punc = {
    "they", "send", "he", "run", "in", "the", "evening", "."
  };
  private static final String[] SENTENCE_maxent_punc = {
    "they", "send", "he", "runn", "in", "the", "evening", "."
  };
  private static final String[] SENTENCE_posTags = {
    "NNP", "VBD", "PRP", "VBG", "IN", "DT", "NN", "."
  };

  private static final String SENTENCES =
      "They sent him running in the evening. He did not come back.";
  private static final String[] SENTENCES_dict_punc = {
    "they", "send", "he", "run", "in", "the", "evening", ".", "he", "do", "not", "come", "back", "."
  };
  private static final String[] SENTENCES_maxent_punc = {
    "they", "send", "he", "runn", "in", "the", "evening", ".", "he", "do", "not", "come", "back",
    "."
  };
  private static final String[] SENTENCES_posTags = {
    "NNP", "VBD", "PRP", "VBG", "IN", "DT", "NN", ".", "PRP", "VBD", "RB", "VB", "RB", "."
  };

  private static final String SENTENCE_both = "Konstantin Kalashnitsov constantly caliphed.";
  private static final String[] SENTENCE_both_punc = {
    "konstantin", "kalashnitsov", "constantly", "caliph", "."
  };
  private static final String[] SENTENCE_both_posTags = {"IN", "JJ", "NN", "VBN", "."};

  private static final String SENTENCES_both =
      "Konstantin Kalashnitsov constantly caliphed. Coreena could care, completely.";
  private static final String[] SENTENCES_both_punc = {
    "konstantin",
    "kalashnitsov",
    "constantly",
    "caliph",
    ".",
    "coreena",
    "could",
    "care",
    ",",
    "completely",
    "."
  };
  private static final String[] SENTENCES_both_posTags = {
    "IN", "JJ", "NN", "VBN", ".", "NNP", "VBN", "NN", ",", "NN", "."
  };

  private static final String[] SENTENCES_dict_keep_orig_punc = {
    "They", "they", "sent", "send", "him", "he", "running", "run", "in", "the", "evening", ".",
    "He", "he", "did", "do", "not", "come", "back", "."
  };
  private static final String[] SENTENCES_max_ent_keep_orig_punc = {
    "They", "they", "sent", "send", "him", "he", "running", "runn", "in", "the", "evening", ".",
    "He", "he", "did", "do", "not", "come", "back", "."
  };
  private static final String[] SENTENCES_keep_orig_posTags = {
    "NNP", "NNP", "VBD", "VBD", "PRP", "PRP", "VBG", "VBG", "IN", "DT", "NN", ".", "PRP", "PRP",
    "VBD", "VBD", "RB", "VB", "RB", "."
  };

  private static final String[] SENTENCES_both_keep_orig_punc = {
    "Konstantin",
    "konstantin",
    "Kalashnitsov",
    "kalashnitsov",
    "constantly",
    "caliphed",
    "caliph",
    ".",
    "Coreena",
    "coreena",
    "could",
    "care",
    ",",
    "completely",
    "."
  };
  private static final String[] SENTENCES_both_keep_orig_posTags = {
    "IN", "IN", "JJ", "JJ", "NN", "VBN", "VBN", ".", "NNP", "NNP", "VBN", "NN", ",", "NN", "."
  };

  private static final String NO_BREAK_SINGLE_TOKEN_REPEAT_KEYWORD = "period";

  private static final String[] NO_BREAK_SINGLE_TOKEN_REPEAT_KEYWORD_terms = {"period", "period"};

  private static final String tokenizerModelFile = "en-test-tokenizer.bin";
  private static final String sentenceModelFile = "en-test-sent.bin";
  private static final String posTaggerModelFile = "en-test-pos-maxent.bin";
  private static final String lemmatizerModelFile = "en-test-lemmatizer.bin";
  private static final String lemmatizerDictFile = "en-test-lemmas.dict";

  public void test1SentenceDictionaryOnly() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", "en-test-pos-maxent.bin")
            .addTokenFilter("opennlplemmatizer", "dictionary", "en-test-lemmas.dict")
            .build();
    assertAnalyzesTo(
        analyzer, SENTENCE, SENTENCE_dict_punc, null, null, SENTENCE_posTags, null, null, true);
  }

  public void test2SentencesDictionaryOnly() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter("opennlplemmatizer", "dictionary", lemmatizerDictFile)
            .build();
    assertAnalyzesTo(
        analyzer, SENTENCES, SENTENCES_dict_punc, null, null, SENTENCES_posTags, null, null, true);
  }

  public void test1SentenceMaxEntOnly() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter("opennlplemmatizer", "lemmatizerModel", lemmatizerModelFile)
            .build();
    assertAnalyzesTo(
        analyzer, SENTENCE, SENTENCE_maxent_punc, null, null, SENTENCE_posTags, null, null, true);
  }

  public void test2SentencesMaxEntOnly() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter("OpenNLPLemmatizer", "lemmatizerModel", lemmatizerModelFile)
            .build();
    assertAnalyzesTo(
        analyzer,
        SENTENCES,
        SENTENCES_maxent_punc,
        null,
        null,
        SENTENCES_posTags,
        null,
        null,
        true);
  }

  public void test1SentenceDictionaryAndMaxEnt() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", "en-test-pos-maxent.bin")
            .addTokenFilter(
                "opennlplemmatizer",
                "dictionary",
                "en-test-lemmas.dict",
                "lemmatizerModel",
                lemmatizerModelFile)
            .build();
    assertAnalyzesTo(
        analyzer,
        SENTENCE_both,
        SENTENCE_both_punc,
        null,
        null,
        SENTENCE_both_posTags,
        null,
        null,
        true);
  }

  public void test2SentencesDictionaryAndMaxEnt() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter(
                "opennlplemmatizer",
                "dictionary",
                lemmatizerDictFile,
                "lemmatizerModel",
                lemmatizerModelFile)
            .build();
    assertAnalyzesTo(
        analyzer,
        SENTENCES_both,
        SENTENCES_both_punc,
        null,
        null,
        SENTENCES_both_posTags,
        null,
        null,
        true);
  }

  public void testKeywordAttributeAwarenessDictionaryOnly() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter(KeywordRepeatFilterFactory.class)
            .addTokenFilter("opennlplemmatizer", "dictionary", lemmatizerDictFile)
            .addTokenFilter(RemoveDuplicatesTokenFilterFactory.class)
            .build();
    assertAnalyzesTo(
        analyzer,
        SENTENCES,
        SENTENCES_dict_keep_orig_punc,
        null,
        null,
        SENTENCES_keep_orig_posTags,
        null,
        null,
        true);
  }

  public void testKeywordAttributeAwarenessMaxEntOnly() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter(KeywordRepeatFilterFactory.class)
            .addTokenFilter("opennlplemmatizer", "lemmatizerModel", lemmatizerModelFile)
            .addTokenFilter(RemoveDuplicatesTokenFilterFactory.class)
            .build();
    assertAnalyzesTo(
        analyzer,
        SENTENCES,
        SENTENCES_max_ent_keep_orig_punc,
        null,
        null,
        SENTENCES_keep_orig_posTags,
        null,
        null,
        true);
  }

  public void testKeywordAttributeAwarenessDictionaryAndMaxEnt() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", posTaggerModelFile)
            .addTokenFilter(KeywordRepeatFilterFactory.class)
            .addTokenFilter(
                "opennlplemmatizer",
                "dictionary",
                lemmatizerDictFile,
                "lemmatizerModel",
                lemmatizerModelFile)
            .addTokenFilter(RemoveDuplicatesTokenFilterFactory.class)
            .build();
    assertAnalyzesTo(
        analyzer,
        SENTENCES_both,
        SENTENCES_both_keep_orig_punc,
        null,
        null,
        SENTENCES_both_keep_orig_posTags,
        null,
        null,
        true);
  }

  public void testNoBreakWithRepeatKeywordFilter() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", "en-test-pos-maxent.bin")
            .addTokenFilter(KeywordRepeatFilterFactory.class)
            .addTokenFilter("opennlplemmatizer", "dictionary", "en-test-lemmas.dict")
            .build();
    assertAnalyzesTo(
        analyzer,
        NO_BREAK_SINGLE_TOKEN_REPEAT_KEYWORD,
        NO_BREAK_SINGLE_TOKEN_REPEAT_KEYWORD_terms,
        null,
        null,
        null,
        null,
        null,
        true);
  }

  // checks for bug described in https://github.com/apache/lucene/issues/11771
  public void testPreventEarlyExit() throws IOException {
    InputStream earlyExitInput = null;
    InputStream earlyExitOutput = null;
    try {
      ClasspathResourceLoader loader = new ClasspathResourceLoader(getClass());
      earlyExitInput = loader.openResource("data/early-exit-bug-input.txt");
      String earlyExitInputText = new String(earlyExitInput.readAllBytes(), StandardCharsets.UTF_8);
      earlyExitOutput = loader.openResource("data/early-exit-bug-output.txt");
      String earlyExitOutputText =
          new String(earlyExitOutput.readAllBytes(), StandardCharsets.UTF_8);
      String[] earlyExitOutputTexts = earlyExitOutputText.split("[\\s\\r\\n]+");

      CustomAnalyzer analyzer =
          CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
              .withTokenizer(
                  "opennlp",
                  "tokenizerModel",
                  tokenizerModelFile,
                  "sentenceModel",
                  sentenceModelFile)
              .addTokenFilter("opennlpPOS", "posTaggerModel", "en-test-pos-maxent.bin")
              .addTokenFilter(KeywordRepeatFilterFactory.class)
              .addTokenFilter("opennlplemmatizer", "dictionary", "en-test-lemmas.dict")
              .build();
      assertAnalyzesTo(
          analyzer, earlyExitInputText, earlyExitOutputTexts, null, null, null, null, null, true);
    } finally {
      if (earlyExitInput != null) {
        earlyExitInput.close();
      }
      if (earlyExitOutput != null) {
        earlyExitOutput.close();
      }
    }
  }

  public void testEmptyField() throws Exception {
    CustomAnalyzer analyzer =
        CustomAnalyzer.builder(new ClasspathResourceLoader(getClass()))
            .withTokenizer(
                "opennlp", "tokenizerModel", tokenizerModelFile, "sentenceModel", sentenceModelFile)
            .addTokenFilter("opennlpPOS", "posTaggerModel", "en-test-pos-maxent.bin")
            .addTokenFilter(KeywordRepeatFilterFactory.class)
            .addTokenFilter("opennlplemmatizer", "dictionary", "en-test-lemmas.dict")
            .build();
    assertAnalyzesTo(analyzer, "", new String[0], null, null, null, null, null, true);
  }
}
